關(guān)于useState

函數(shù)組件

function App() {

? const [num, updateNum] = useState(0);

? window.updateNum = updateNum;

? return num;

}

調(diào)用window.updateNum(1)可以將視圖中的0更新為1

對于如下函數(shù)組件

function App() {

? const [num, updateNum] = useState(0);

?

? function increment() {

? ? setTimeout(() => {

? ? ? updateNum(num + 1);

? ? }, 1000);

? }

?

? return <p onClick={increment}>{num}</p>;

}

點(diǎn)擊5次 結(jié)果仍為1


hook如何保存數(shù)據(jù)

FunctionComponent的render本身只是函數(shù)調(diào)用。

那么在render內(nèi)部調(diào)用的hook是如何獲取到對應(yīng)數(shù)據(jù)呢?

比如:

useState獲取state

useRef獲取ref

useMemo獲取緩存的數(shù)據(jù)

答案是:

每個組件有個對應(yīng)的fiber節(jié)點(diǎn)(可以理解為虛擬DOM),用于保存組件相關(guān)信息。

每次FunctionComponent render時,全局變量currentlyRenderingFiber都會被賦值為該FunctionComponent對應(yīng)的fiber節(jié)點(diǎn)。

所以,hook內(nèi)部其實(shí)是從currentlyRenderingFiber中獲取狀態(tài)信息的。


多個hook如何獲取數(shù)據(jù)

我們知道,一個FunctionComponent中可能存在多個hook,比如:

function App() {

? // hookA

? const [a, updateA] = useState(0);

? // hookB

? const [b, updateB] = useState(0);

? // hookC

? const ref = useRef(0);

?

? return <p></p>;

}

那么多個hook如何獲取自己的數(shù)據(jù)呢?

答案是:

currentlyRenderingFiber.memoizedState中保存一條hook對應(yīng)數(shù)據(jù)的單向鏈表。

對于如上例子,可以理解為:

const hookA = {

? // hook保存的數(shù)據(jù)

? memoizedState: null,

? // 指向下一個hook

? next: hookB

? // ...省略其他字段

};

hookB.next = hookC;

currentlyRenderingFiber.memoizedState = hookA;

當(dāng)FunctionComponent render時,每執(zhí)行到一個hook,都會將指向currentlyRenderingFiber.memoizedState鏈表的指針向后移動一次,指向當(dāng)前hook對應(yīng)數(shù)據(jù)。

這也是為什么React要求hook的調(diào)用順序不能改變(不能在條件語句中使用hook) —— 每次render時都是從一條固定順序的鏈表中獲取hook對應(yīng)數(shù)據(jù)的。

useState執(zhí)行流程

我們知道,useState返回值數(shù)組第二個參數(shù)為改變state的方法。

在源碼中,他被稱為dispatchAction。

每當(dāng)調(diào)用dispatchAction,都會創(chuàng)建一個代表一次更新的對象update:

const update = {

? // 更新的數(shù)據(jù)

? action: action,

? // 指向下一個更新

? next: null

};

對于如下例子

function App() {

? const [num, updateNum] = useState(0);

?

? function increment() {

? ? updateNum(num + 1);

? }

?

? return <p onClick={increment}>{num}</p>;

}

調(diào)用updateNum(num + 1),會創(chuàng)建:

const update = {

? // 更新的數(shù)據(jù)

? action: 1,

? // 指向下一個更新

? next: null

? // ...省略其他字段

};

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容