大家好,我是若川。今天分享一篇關于「React?Hooks」的好文。歡迎點擊下方卡片關注我。
以下是正文~
回想下你入門Hooks
的過程,是不是經歷過:
類比
ClassComponent
的生命周期,學習Hooks
的執行時機慢慢熟練以后,發現
Hooks
的執行時機和生命周期
又有些不同。比如componentWillReceiveProps
對應哪個Hooks
?感到困惑,去搜一些
Hooks
原理層面的文章閱讀
作為一個API
,不該簡簡單單、可可愛愛的照著文檔調用就行么,Hooks
為什么這么難?

React
官方也發現了這個問題,在React要重寫文檔了講到,React
要基于Hooks
重寫文檔。
本文主要包括2方面內容:
解釋
Hooks
難學的原因給出學習
Hooks
的建議
React的底層架構
可以用一個公式概括React
:
const?UI?=?fn(state);
視圖
可以看作狀態
經過函數
的映射。
用戶與界面的交互,可以看作這個公式的不斷執行。
這個公式太精簡了,沒有解釋state
(狀態)從哪兒來,我們擴展下:
const?state?=?reconcile(update);
const?UI?=?fn(state);
用戶交互產生
update
(更新)update
經過reconcile
步驟計算出當前應用的state
fn
將state
映射為視圖變化(UI)
我們給fn
起個名字:commit
:
const?state?=?reconcile(update);
const?UI?=?commit(state);
那么update
在哪里產生呢?當然來自于用戶交互,比如:點擊事件。
所以React
的底層架構可以簡化為三步:
用戶交互產生
update
state
= reconcile(update
);UI
= commit(state
);
了解了底層架構,我們再來看通過類比ClassComponent
學習Hooks
會帶來的問題。
生命周期函數的抽象層級
我們已經有了完整的驅動視圖更新的底層架構,開發者該怎么操作這套架構呢?
可以用計算機的抽象層級來類比:
高層:應用程序
中層:操作系統
底層:計算機組成架構
對應React
:
高層:應用程序?????? ClassComponent生命周期
中層:操作系統???????介入架構的API
底層:計算機組成架構? React底層架構
可以看到,生命周期函數
屬于抽象程度比較高的層次。這么設計也是為了讓開發者更容易上手React
。
設想一個Vue2
開發者要轉React
技術棧,只需要類比Vue
的生命周期來學習React
的生命周期就行了。
這一切在Hooks
到來前都沒問題,然而......
Hooks的抽象層級
Hooks
屬于中等抽象層級。也就是說,Hooks
直接介入底層架構的運行流程。
高層:應用程序???????
中層:操作系統?????? Hooks
底層:計算機組成架構? React底層架構??????
當我們用生命周期函數
來類比Hooks
時,其實是用高抽象層級
的事物來描述低抽象層級
的事物。
動物 --> 哺乳動物 --> 牛 --> 奶牛
對于一個只見過奶牛,再沒見過其他動物的人,你怎么向他解釋哺乳動物
是啥?

正是由于抽象層級的不對稱,造成通過生命周期函數
類比學習Hooks
會遇到問題。
該怎么學Hooks
既然Hooks
屬于中等抽象層,離底層很近,那么更好的學習方式是通過底層向上學習。
祭出我們的三步公式:
用戶交互產生
update
state
= reconcile(update
);UI
= commit(state
);
對照公式,我們來講解幾個常見hook
的工作流程:
useState
舉個例子:
function?App()?{const?[state,?updateState]?=?useState(0);return?<div?onClick={()?=>?updateState(state?+?1)}></div>;
}
useState
返回值數組包含:
保存的
state
改變
state
的方法updateState
對照公式,state
屬于公式步驟2計算得出的:
state = reconcile(update);
此時視圖還沒有更新。
用戶點擊div
觸發updateState
,對應公式步驟1:
用戶交互產生
update
所以調用updateState
能開啟底層架構的三步運行流程。
當reconcile
計算出state
后就會進入第三步:
UI
= commit(state
);
最終渲染視圖。
useEffect
舉個例子:
useEffect(doSomething,?[xx,?yy])
useEffect
的回調函數doSomething
在第三步執行完成后異步調用:
UI
= commit(state
);
所以在doSomething
函數內部能獲取到完成更新的視圖。
第二個參數[xx, yy]
作為依賴項,決定了doSomething
是否會被調用。
useLayoutEffect
不同于useEffect
在第三步執行完成后異步調用,useLayoutEffect
會在第三步執行完UI
操作后同步執行。
useRef
以上例子可以看到,useState
與useEffect
分別在三步流程的不同步驟被觸發,他們的觸發時機是確定的。
那么這三個步驟如何交流呢?通過useRef
。
useState
作用于第一、二步,useLayoutEffect
作用于第三步,useEffect
作用于第三步完成后。
使用useRef
,就能達到在不同步驟間共享引用類型數據的目的。
可以看到,React
為底層架構三步工作流程的每一步提供了對應的hook
,同時提供了串聯這三步工作流程的hook
。
開發者只需要根據業務需要,通過基礎Hooks
組裝出自定義hook
,就能在底層架構運行流程的各個時期運行邏輯。
自底向上學習是本末倒置么?
有同學會反駁:之前學React
得學生命周期函數
的執行時機,現在學Hooks
得學底層架構運行流程
。難道不是本末倒置,更復雜了么?
其實不然。我問你幾個問題:
componentWillReceiveProps
為什么被標記為unsafe
?getDerivedStateFromProps
用過么?this.setState
是同步還是異步的?
這些和生命周期函數
相關的問題一點都不簡單!很多用了幾年React
的前端不一定回答的上。
作為高層次抽象,生命周期函數
隱藏了太多實現細節。同時React
又太靈活,不像Vue
通過模版語言
限制了開發者的操作。
結果就是:不同React
開發者寫出各種奇奇怪怪的ClassComponent
。
反觀通過底層架構運行流程
學習Hooks
:
底層架構運行流程
就是React
的絕對真理,不會隱藏更多抽象Hooks
的寫法規范限制了開發者的奇葩操作
這里唯一的問題,就是缺少一份從底層
出發的文檔。這也是官方要重寫文檔的初衷。
對于熟練使用React
的開發者,在官方新文檔出來前,可以參考React技術揭秘[1](點擊閱讀原文)學習。
這里再提供些其他視角聊Hooks
的文章:
從
理念
層面:代數效應與Hooks[2]從
微觀
(代碼)層面:所有常見Hooks的源碼實現[3]
參考資料
[1]
React技術揭秘: https://react.iamkasong.com/
[2]代數效應與Hooks: https://react.iamkasong.com/process/fiber-mental.html
[3]所有常見Hooks的源碼實現: https://react.iamkasong.com/hooks/structure.html
最近組建了一個江西人的前端交流群,如果你也是江西人可以加我微信 ruochuan12 拉你進群。
·················?若川出品?·················
今日話題
vue-devtools工具中可以「根據組件直接打開對應文件」,多少人知道或者用過呢。
一個愿景是幫助5年內前端人走向前列的公眾號
可加我個人微信 ruochuan12,長期交流學習
推薦閱讀
我在阿里招前端,我該怎么幫你?(現在還能加我進模擬面試群)
如何拿下阿里巴巴 P6 的前端 Offer