前言:Hooks已經出現(xiàn)很久了,它的出現(xiàn)原因在這里就不再贅述,我們可以在官網看到很詳細的解釋,今天來總結下hooks是怎么實現(xiàn)的。
hooks api是怎么設計的
這個我們從代碼的運行和源碼兩方面來看下。
同一個函數在掛載和更新階段為什么結果不一樣
function Component() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('hello world!');
}, []);
return (
<div>
<div id="count-text">count = {count}</div>
<div><button id="btn" onClick={setCount(count+1)}>按鈕</button></div>
</div>
);
}
上面這段代碼,我們都知道count在初始化時是0,點擊按鈕更新后每次都加1,下面是一個簡單的運行流程圖:

image.png
我們可以看到初次渲染時執(zhí)行了render方法,在組件內部執(zhí)行了useState函數,在調用setState后又重新走一遍組件的渲染流程,同樣也執(zhí)行了useState方法,這次卻拿到了newState。同樣的函數,同樣的傳參為什么會得到不同的值呢?你肯定會一下子就想到了,當然是因為mount和update生命周期不同啊,那么我們就看下源碼是怎么實現(xiàn)的吧。
mount階段和update階段分別做了什么呢
很容易就可以找到源碼,如下:
export function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
export function useEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
const dispatcher = resolveDispatcher();
return dispatcher.useEffect(create, deps);
}
如上是useState和useEffect兩個api最終暴露的源碼,可以看到返回的函數是掛載在dispatcher對象上的,我們再往下看一下就會發(fā)現(xiàn)不一樣了。如下:
export function renderWithHook(workInProgress: FiberNode, render: any) {
currentlyFiber = workInProgress;
if (currentlyFiber.memoizedState === null) {
dispatcher.current = HooksDispatcherOnMountInDEV;
} else {
dispatcher.current = HooksDispatcherOnUpdateInDEV;
}
return render(); // 里面包含節(jié)點渲染的調度
}
源碼比較長,我這里精簡了很多,可以看到在渲染組件時,判斷Fiber節(jié)點是否存在hooks鏈表,以此來判斷組件目前處于mount還是update階段,從而給dispatcher對象掛載不同的hooks函數。函數具體如下:
// hook掛載時的api
const HooksDispatcherOnMountInDEV = {
useState: (initialState) => {
return mountState(initialState);
},
useEffect: (create, deps) => {
return mountEffect(create, deps);
},
}
// hook更新時的api
const HooksDispatcherOnUpdateInDEV = {
useState: (initialState) => {
return updateState(initialState);
},
useEffect: (create, deps) => {
return updateEffect(create, deps);
}
}
從上面可以看出,mount階段和update階段執(zhí)行的底層函數是不同的,mount階段對應mounXXX函數,update階段對應updateXXX函數。
因此,從代碼的運行和源碼我們可以得到如下結論:
- hooks函數不同生命周期,對應的底層函數不同;
- 將不同生命周期執(zhí)行的
底層函數都掛載在了dispatcher對象上,我們在使用時不用去處理生命周期的差異,只用專注于我們的業(yè)務邏輯;
這篇文章會分為三章來寫,今天只是簡單的寫個引子,后面請繼續(xù)關注二哈!上面關于
hooks鏈表這個名詞,接下來第二章解釋哈~