實作 React Hooks
UI 開發有兩個問題:
- 展示復用
- 邏輯復用
展示復用目前基本使用組件化來解決,邏輯復用一直以來都沒有特別好的解決方案,React 從一開始的 mixin ,到 高階組件 以及 Render Props ,都是在試圖解決這個問題,但是都引入了一些別的問題,
Mixins
- 命名空間沖突
- 資料來源不清晰
Higher-order Components
- props 屬性來源不清晰
- props 上命名沖突
- 額外的組件渲染帶來性能問題
Render Props(Vue 中的 Renderless Components)
- 解決了 命名空間沖突、資料來源不清晰的問題,仍然會帶來額外組件實體的性能消耗
Hooks
在前段時間 Hooks 發布后,我認為 React 找到了【有狀態】組件【函式式】【復用邏輯】的解決方案,
先說有狀態:一般來說,無狀態組件直接使用函陣列件就行,省去了實體化的樣板代碼和性能消耗,不涉及到 state 的存取,可以直接寫個 helper 函式處理一下,方便又快捷,
再說函式式:class 組件是面向物件的,每一次宣告、宣告周期都逃不開 this,而 hooks 更加函式式,呼叫一個函式,傳入的是初始值,回傳修改值,沒有副作用,
最后說復用邏輯:DRY,一般來說,相同的代碼不寫第二次,在 class 組件中,通過生命周期方法對 state 修改,然后 rerender,在使用了 hooks 以后,我們可以通過 hooks 觸發 render,render 呼叫 hooks 時,根據傳入值的比較,來決定是否觸發 render,然后把 hooks 回傳的值填充到頁面上,
實作
hooks 給人最直接的印象就是可以在 function 組件中使用 state 了,但它也有著不同于 class 組件的心智模型,從生命周期的思維跳到 update circle,剛開始 useEffect 總會寫一些無限回圈,接下來我們先來寫一個簡易版的 useState,來模仿 React,
let state;
function useState(init) {
function setState(newState) {
if (typeof newState === "function") {
// 支持舊值傳入更新
state = newState(state);
} else {
state = newState;
}
// setState 后呼叫組件 render
// render();
}
if (!state) {
state = init;
}
return [state, setState];
}
上面就是一個 useState 了,接下來繼續寫 useEffect.
let _deps;
function useEffect(callback, deps) {
if (
deps === undefined || // 就是不傳 deps,就是每次都執行副作用
(deps !== undefined && _deps === undefined) || // 就是初始化,要執行一次副作用
!deps.every((dep, i) => dep === _deps[i]) // 如果不是每一項都相等,就執行
) {
callback();
}
}
這就是 useEffect 基本實作了,當然還有一個問題,callback 的 return 函式問題,
let _deps;
function useEffect(callback, deps) {
if (
deps === undefined || // 就是不傳 deps,就是每次都執行副作用
(deps !== undefined && _deps === undefined) || // 就是初始化,要執行一次副作用
!deps.every((dep, i) => dep === _deps[i]) // 如果不是每一項都相等,就執行
) {
// 如果有 cleanUp 就執行清理
if (typeof _deps._cleanUp === "function") {
_deps._cleanUp();
}
_deps._cleanUp = callback();
}
}
接下來,還有個重要問題,hooks 的順序問題,其實就是把 state 和 deps 存到陣列里,
let _arr = [];
let cursor = 0;
function useState(init) {
const current = cursor;
function setState(newState) {
if (typeof newState === "function") {
// 支持舊值傳入更新
_arr[current] = newState(_arr[current]);
} else {
_arr[current] = newState;
}
// setState 后呼叫組件 render
render();
}
if (!_arr[current]) {
_arr[current] = init;
}
cursor++;
return [_arr[current], setState];
}
function useEffect(callback, deps) {
const effect = _arr[cursor] || {};
const { _deps, _cleanUp } = effect;
if (
deps === undefined || // 就是不傳 deps,就是每次都執行副作用
(deps !== undefined && _deps === undefined) || // 就是初始化,要執行一次副作用
!deps.every((dep, i) => dep === _deps[i]) // 如果不是每一項都相等,就執行
) {
// 如果有 cleanUp 就執行清理
if (typeof _cleanUp === "function") {
_cleanUp();
}
effect._cleanUp = callback();
effect._deps = deps;
_arr[cursor] = effect;
}
cursor++;
}
全部代碼在 CodeSandBox,
完,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/45603.html
標籤:JavaScript
上一篇:屏蔽網頁右鍵
