跟著官方文檔能學(xué)懂Hooks就怪了
回想下你入門Hooks的過(guò)程,是不是經(jīng)歷過(guò):
- 類比ClassComponent的生命周期,學(xué)習(xí)Hooks的執(zhí)行時(shí)機(jī)
 - 慢慢熟練以后,發(fā)現(xiàn)Hooks的執(zhí)行時(shí)機(jī)和生命周期又有些不同。比如componentWillReceiveProps對(duì)應(yīng)哪個(gè)Hooks?
 - 感到困惑,去搜一些Hooks原理層面的文章閱讀
 
作為一個(gè)API,不該簡(jiǎn)簡(jiǎn)單單、可可愛(ài)愛(ài)的照著文檔調(diào)用就行么,Hooks為什么這么難?
React官方也發(fā)現(xiàn)了這個(gè)問(wèn)題,在React要重寫文檔了講到,React要基于Hooks重寫文檔。
本文主要包括2方面內(nèi)容:
- 解釋Hooks難學(xué)的原因
 - 給出學(xué)習(xí)Hooks的建議
 
React的底層架構(gòu)
可以用一個(gè)公式概括React:
- const UI = fn(state);
 
視圖可以看作狀態(tài)經(jīng)過(guò)函數(shù)的映射。
用戶與界面的交互,可以看作這個(gè)公式的不斷執(zhí)行。
這個(gè)公式太精簡(jiǎn)了,沒(méi)有解釋state(狀態(tài))從哪兒來(lái),我們擴(kuò)展下:
- const state = reconcile(update);
 - const UI = fn(state);
 
- 用戶交互產(chǎn)生update(更新)
 - update經(jīng)過(guò)reconcile步驟計(jì)算出當(dāng)前應(yīng)用的state
 - fn將state映射為視圖變化(UI)
 
我們給fn起個(gè)名字:commit:
- const state = reconcile(update);
 - const UI = commit(state);
 
那么update在哪里產(chǎn)生呢?當(dāng)然來(lái)自于用戶交互,比如:點(diǎn)擊事件。
所以React的底層架構(gòu)可以簡(jiǎn)化為三步:
- 用戶交互產(chǎn)生update
 - state = reconcile(update);
 - UI = commit(state);
 
了解了底層架構(gòu),我們?cè)賮?lái)看通過(guò)類比ClassComponent學(xué)習(xí)Hooks會(huì)帶來(lái)的問(wèn)題。
生命周期函數(shù)的抽象層級(jí)
我們已經(jīng)有了完整的驅(qū)動(dòng)視圖更新的底層架構(gòu),開(kāi)發(fā)者該怎么操作這套架構(gòu)呢?
可以用計(jì)算機(jī)的抽象層級(jí)來(lái)類比:
- 高層:應(yīng)用程序
 - 中層:操作系統(tǒng)
 - 底層:計(jì)算機(jī)組成架構(gòu)
 
對(duì)應(yīng)React:
- 高層:應(yīng)用程序 ClassComponent生命周期
 - 中層:操作系統(tǒng) 介入架構(gòu)的API
 - 底層:計(jì)算機(jī)組成架構(gòu) React底層架構(gòu)
 
可以看到,生命周期函數(shù)屬于抽象程度比較高的層次。這么設(shè)計(jì)也是為了讓開(kāi)發(fā)者更容易上手React。
設(shè)想一個(gè)Vue2開(kāi)發(fā)者要轉(zhuǎn)React技術(shù)棧,只需要類比Vue的生命周期來(lái)學(xué)習(xí)React的生命周期就行了。
這一切在Hooks到來(lái)前都沒(méi)問(wèn)題,然而......
Hooks的抽象層級(jí)
Hooks屬于中等抽象層級(jí)。也就是說(shuō),Hooks直接介入底層架構(gòu)的運(yùn)行流程。
- 高層:應(yīng)用程序
 - 中層:操作系統(tǒng) Hooks
 - 底層:計(jì)算機(jī)組成架構(gòu) React底層架構(gòu)
 
當(dāng)我們用生命周期函數(shù)來(lái)類比Hooks時(shí),其實(shí)是用高抽象層級(jí)的事物來(lái)描述低抽象層級(jí)的事物。
動(dòng)物 --> 哺乳動(dòng)物 --> 牛 --> 奶牛
對(duì)于一個(gè)只見(jiàn)過(guò)奶牛,再?zèng)]見(jiàn)過(guò)其他動(dòng)物的人,你怎么向他解釋哺乳動(dòng)物是啥?
正是由于抽象層級(jí)的不對(duì)稱,造成通過(guò)生命周期函數(shù)類比學(xué)習(xí)Hooks會(huì)遇到問(wèn)題。
該怎么學(xué)Hooks
既然Hooks屬于中等抽象層,離底層很近,那么更好的學(xué)習(xí)方式是通過(guò)底層向上學(xué)習(xí)。
祭出我們的三步公式:
- 用戶交互產(chǎn)生update
 - state = reconcile(update);
 - UI = commit(state);
 
對(duì)照公式,我們來(lái)講解幾個(gè)常見(jiàn)hook的工作流程:
useState
舉個(gè)例子:
- function App() {
 - const [state, updateState] = useState(0);
 - return <div onClick={() => updateState(state + 1)}></div>;
 - }
 
useState返回值數(shù)組包含:
- 保存的state
 - 改變state的方法updateState
 
對(duì)照公式,state屬于公式步驟2計(jì)算得出的:
- state = reconcile(update);
 
此時(shí)視圖還沒(méi)有更新。
- 用戶點(diǎn)擊div觸發(fā)updateState,對(duì)應(yīng)公式步驟1:
 
用戶交互產(chǎn)生update
所以調(diào)用updateState能開(kāi)啟底層架構(gòu)的三步運(yùn)行流程。
當(dāng)reconcile計(jì)算出state后就會(huì)進(jìn)入第三步:
- UI = commit(state);
 
最終渲染視圖。
useEffect
舉個(gè)例子:
- useEffect(doSomething, [xx, yy])
 
useEffect的回調(diào)函數(shù)doSomething在第三步執(zhí)行完成后異步調(diào)用:
- UI = commit(state);
 
所以在doSomething函數(shù)內(nèi)部能獲取到完成更新的視圖。
第二個(gè)參數(shù)[xx, yy]作為依賴項(xiàng),決定了doSomething是否會(huì)被調(diào)用。
useLayout
Effect不同于useEffect在第三步執(zhí)行完成后異步調(diào)用,useLayoutEffect會(huì)在第三步執(zhí)行完UI操作后同步執(zhí)行。
useRef
以上例子可以看到,useState與useEffect分別在三步流程的不同步驟被觸發(fā),他們的觸發(fā)時(shí)機(jī)是確定的。
那么這三個(gè)步驟如何交流呢?通過(guò)useRef。
useState作用于第一、二步,useLayoutEffect作用于第三步,useEffect作用于第三步完成后。
使用useRef,就能達(dá)到在不同步驟間共享引用類型數(shù)據(jù)的目的。
可以看到,React為底層架構(gòu)三步工作流程的每一步提供了對(duì)應(yīng)的hook,同時(shí)提供了串聯(lián)這三步工作流程的hook。
開(kāi)發(fā)者只需要根據(jù)業(yè)務(wù)需要,通過(guò)基礎(chǔ)Hooks組裝出自定義hook,就能在底層架構(gòu)運(yùn)行流程的各個(gè)時(shí)期運(yùn)行邏輯。
自底向上學(xué)習(xí)是本末倒置么?
有同學(xué)會(huì)反駁:之前學(xué)React得學(xué)生命周期函數(shù)的執(zhí)行時(shí)機(jī),現(xiàn)在學(xué)Hooks得學(xué)底層架構(gòu)運(yùn)行流程。難道不是本末倒置,更復(fù)雜了么?
其實(shí)不然。我問(wèn)你幾個(gè)問(wèn)題:
- componentWillReceiveProps為什么被標(biāo)記為unsafe?
 - getDerivedStateFromProps用過(guò)么?
 - this.setState是同步還是異步的?
 
這些和生命周期函數(shù)相關(guān)的問(wèn)題一點(diǎn)都不簡(jiǎn)單!很多用了幾年React的前端不一定回答的上。
作為高層次抽象,生命周期函數(shù)隱藏了太多實(shí)現(xiàn)細(xì)節(jié)。同時(shí)React又太靈活,不像Vue通過(guò)模版語(yǔ)言限制了開(kāi)發(fā)者的操作。
結(jié)果就是:不同React開(kāi)發(fā)者寫出各種奇奇怪怪的ClassComponent。
反觀通過(guò)底層架構(gòu)運(yùn)行流程學(xué)習(xí)Hooks:
- 底層架構(gòu)運(yùn)行流程就是React的絕對(duì)真理,不會(huì)隱藏更多抽象
 - Hooks的寫法規(guī)范限制了開(kāi)發(fā)者的奇葩操作
 
這里唯一的問(wèn)題,就是缺少一份從底層出發(fā)的文檔。這也是官方要重寫文檔的初衷。
對(duì)于熟練使用React的開(kāi)發(fā)者,在官方新文檔出來(lái)前,可以參考React技術(shù)揭秘[1](點(diǎn)擊閱讀原文)學(xué)習(xí)。
這里再提供些其他視角聊Hooks的文章:
- 從理念層面:代數(shù)效應(yīng)與Hooks[2]
 - 從微觀(代碼)層面:所有常見(jiàn)Hooks的源碼實(shí)現(xiàn)[3]
 
















 
 
 







 
 
 
 