成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

從源碼理解 React Hook 是如何工作的

開發(fā) 前端
本文只講了狀態(tài) Hook 代表 UseState,和 副作用 Hook 代表 UseEffect,其他 Hook 其實(shí)也差不多。

大家好,我是前端西瓜哥。

今天我們從源碼來理解 React Hook 是如何工作的。

React Hook 是 React 16.8 后新加入的黑魔法,讓我們可以 在函數(shù)組件內(nèi)保存內(nèi)部狀態(tài)

Hook 的優(yōu)勢(shì):

  1. 比組件更小粒度的復(fù)用,之前復(fù)用需要用 Mixin 或 高階組件(HOC,一個(gè)能夠返回組件的組件)進(jìn)行封裝,前者依賴關(guān)系隱式導(dǎo)致難以維護(hù),后者粒度過大、嵌套過深。
  2. 將處理同一個(gè)邏輯的業(yè)務(wù)代碼放在一起,讓代碼可以更好維護(hù)。如果是類組件,得放各個(gè)生命周期函數(shù)中,邏輯會(huì)很分散。
  3. 類組件的 class 寫法容易寫錯(cuò),一不小心 this 就指向錯(cuò)誤,沒錯(cuò)就是說事件響應(yīng)函數(shù)你。另外讀取值也麻煩,要寫很長的this.state.count。
  4. 擁抱函數(shù)式編程,這是 React 團(tuán)隊(duì)所提倡的編程寫法。

一些全局變量

在講解源碼之前,先認(rèn)識(shí)一些 重要的全局變量:

currentlyRenderingFiber:正在處理的函數(shù)組件對(duì)應(yīng) fiber。在執(zhí)行 useState 等 hook 時(shí),需要通過它知道當(dāng)前 hook 對(duì)應(yīng)哪個(gè) fiber。

workInProgressHook:掛載時(shí)正在處理的 hook 對(duì)象。我們會(huì)沿著 workInProcess.memoizedState 鏈表一個(gè)個(gè)往下走,這個(gè) workInProgressHook 就是該鏈表的指針。

currentHook:舊的 fiber 的 hooks 鏈表(current.memorizedState)指針。

ReactCurrentDispatcher:全局對(duì)象,是一個(gè) hook 調(diào)度器對(duì)象,其下有 useState、useEffect 等方法,是我們業(yè)務(wù)代碼中 hook 底層調(diào)用的方法。ReactCurrentDispatcher 有三種:

  1. ContextOnlyDispatcher:所有方法都會(huì)拋出錯(cuò)誤,用于防止開發(fā)者在調(diào)用函數(shù)組件的其他時(shí)機(jī)調(diào)用 React Hook;
  2. HooksDispatcherOnMount:掛載階段用。比如它的 useState 要將初始值保存起來;
  3. HooksDispatcherOnUpdate:更新階段用。比如它的 useState 會(huì)無視傳入的初始值,而是從鏈表中取出值。

renderWithHooks

構(gòu)建函數(shù)實(shí)例是在 renderWithHooks 方法中進(jìn)行的。

主要邏輯為:

  1. workInProgress 賦值給全局變量 currentlyRenderingFiber,之后執(zhí)行 hook 就能知道是給哪個(gè)組件更新狀態(tài)了。
  2. 選擇 hook 調(diào)度器:根據(jù)是掛載還是更新階段,ReactCurrentDispatcher 設(shè)置為對(duì)應(yīng) hook 調(diào)度器。
  3. 調(diào)用函數(shù)組件,進(jìn)行 render。函數(shù)組件內(nèi)部會(huì)調(diào)用 Hook,并返回 ReactElement。
  4. 重置全局變量,比如 currentlyRenderingFiber 設(shè)置回 null;ReactCurrentDispatcher 還原為 ContextOnlyDispatcher,防止在錯(cuò)誤時(shí)機(jī)使用 Hook。
function renderWithHooks(
current,
workInProgress,
Component,
props,
secondArg,
nextRenderLanes
) {
renderLanes = nextRenderLanes;

// 1. 將 workInProgress 賦值給全局變量 currentlyRenderingFiber
// 這樣我們?cè)谡{(diào)用 Hook 時(shí)就能知道對(duì)應(yīng)的 fiber 是誰
currentlyRenderingFiber = workInProgress;

workInProgress.memoizedState = null;
workInProgress.updateQueue = null;
workInProgress.lanes = NoLanes;

// 2. 根據(jù)是掛載還是更新階段,選擇對(duì)應(yīng) hook 調(diào)度器
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;

// 3. 調(diào)用函數(shù)組件,里面執(zhí)行各種 React Hook,并返回 ReactElement
let children = Component(props, secondArg);

// 4. hook 調(diào)度器還原為 ContextOnlyDispatcher
ReactCurrentDispatcher.current = ContextOnlyDispatcher;

const didRenderTooFewHooks =
currentHook !== null && currentHook.next !== null;

// 將一些全局變量進(jìn)行重置
renderLanes = NoLanes;
currentlyRenderingFiber = null;
currentHook = null;
workInProgressHook = null;
didScheduleRenderPhaseUpdate = false;

// Hook 數(shù)量比上次少,對(duì)不上,報(bào)錯(cuò)
if (didRenderTooFewHooks) {
throw new Error(
'Rendered fewer hooks than expected. This may be caused by an accidental ' +
'early return statement.',
);
}

return children;
}

下面看看在函數(shù)組件一些常見 Hook 是如何工作的。

useState

首先討論 狀態(tài) Hook 中最常見的一種:useState。

掛載階段(狀態(tài)初始化)

useState 在掛載階段,調(diào)用的是 HooksDispatcherOnMount.useState,也就是 mountState。

  1. 創(chuàng)建新的 hook 空對(duì)象,掛到 workInProcess.memorizedState 隊(duì)列上(mountWorkInProgressHook 方法)。
  2. dispatchSetState 綁定對(duì)應(yīng) fiber 和 queue,方便以后 setState 快速找到相關(guān)對(duì)象,最后返回狀態(tài)值和更新狀態(tài)方法。
function mountState(initialState) {
// 1. 創(chuàng)建一個(gè) hook 對(duì)象,并添加到 workInProcess.memoizedState 鏈表上
const hook = mountWorkInProgressHook();

// useState 傳入的可能是個(gè)函數(shù),要調(diào)用一下拿到初始值
if (typeof initialState === 'function') {
initialState = initialState();
}

hook.memoizedState = hook.baseState = initialState;

const queue = {
pending: null,
lanes: NoLanes,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: initialState,
};
hook.queue = queue;

// 更新 state 的方法
const dispatch = queue.dispatch = dispatchSetState.bind(
null,
currentlyRenderingFiber,
queue,
);

// 返回我們經(jīng)常用的 [state, setState]
return [hook.memoizedState, dispatch];
}

mountWorkInProgressHook 實(shí)現(xiàn):

function mountWorkInProgressHook() {
// 新的 hook 空對(duì)象
const hook: Hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null,
};

// 給 memoizedState 鏈表加節(jié)點(diǎn)的邏輯
// 寫過單鏈表的會(huì)比較理解,頭節(jié)點(diǎn)要特殊處理
if (workInProgressHook === null) {
currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
} else {
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}

更新狀態(tài)操作(setState)

之前 mountState 時(shí),我們返回了一個(gè)綁定了 fiber、queue 參數(shù)的 dispatchSetState。setState 更新操作調(diào)用的正是這個(gè) dispatchSetState。

第一個(gè) setState 在被調(diào)用時(shí)會(huì)立即計(jì)算新狀態(tài),這是為了 做新舊 state 對(duì)比,決定是否更新組件。如果對(duì)比發(fā)現(xiàn)狀態(tài)沒變,繼續(xù)計(jì)算下一個(gè) setState 的新狀態(tài),直到找到為止。如果沒找到,就不進(jìn)行更新。

其后的 setState 則不會(huì)計(jì)算,等到組件重新 render 再計(jì)算。

為對(duì)比新舊狀態(tài)計(jì)算出來的狀態(tài)值,會(huì)保存到 update.eagerState,并將 update.hasEagerState 設(shè)置為 true,之后更新時(shí)通過它來直接拿到計(jì)算后的最新值。

dispatchSetState 會(huì)拿到對(duì)應(yīng)的 fiber、queue(對(duì)應(yīng) hook 的 queue)、action(新的狀態(tài))。

  1. 創(chuàng)建一個(gè) update 空對(duì)象;
  2. 計(jì)算出最新狀態(tài),放入到 update.egerState。
  3. 對(duì)比新舊狀態(tài)是否相同(使用 Object.is 對(duì)比)。相同就不更新了,結(jié)束。不相同,進(jìn)行后續(xù)的操作。
  4. 將 update 放到 queue.interleaved 或 concurrentQueues 鏈表上(.new 和 .old 文件的邏輯差得有點(diǎn)多),之后更新階段會(huì)搬到 queue.pending。
  5. 將當(dāng)前 fiber 的 lanes 設(shè)置為 SyncLane,這樣后面的 setState 就不會(huì)立刻計(jì)算最新狀態(tài)了,而是在更新階段才計(jì)算。
  6. 接著是調(diào)度更新(scheduleUpdateOnFiber),讓調(diào)度器進(jìn)行調(diào)度,執(zhí)行更新操作。
function dispatchSetState(fiber, queue, action) {
const lane = requestUpdateLane(fiber);

// 創(chuàng)建一個(gè) update 更新對(duì)象
const update = {
lane,
action,
hasEagerState: false,
eagerState: null,
next: null,
};

if (isRenderPhaseUpdate(fiber)) {
// 渲染階段更新,先不討論這種特殊情況
enqueueRenderPhaseUpdate(queue, update);
} else {
const alternate = fiber.alternate;
if (
// 第二次 setState 時(shí),fiber.lanes 為 SyncLane
fiber.lanes === NoLanes &&
(alternate === null || alternate.lanes === NoLanes)
) {
const lastRenderedReducer = queue.lastRenderedReducer;
if (lastRenderedReducer !== null) {
let prevDispatcher;
try {
const currentState = queue.lastRenderedState;
// 計(jì)算新狀態(tài)
const eagerState = lastRenderedReducer(currentState, action);
update.hasEagerState = true;
update.eagerState = eagerState;
// 對(duì)比新舊狀態(tài)是否不同
if (is(eagerState, currentState)) {
// 狀態(tài)沒改變,當(dāng)前 setState 無效,return 結(jié)束,無事發(fā)生
enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update);
return;
}
} catch (error) {
// Suppress the error. It will throw again in the render phase.
}
}
}

// 將 update 加到 queue 鏈表末尾
// 將 fiber 標(biāo)記為 SyncLane
const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
if (root !== null) {
const eventTime = requestEventTime();
// 調(diào)度 fiber 更新
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
entangleTransitionUpdate(root, queue, lane);
}
}
}

更新階段(獲取最新狀態(tài))

我們先了解一個(gè)前置知識(shí):useState 是特殊的 useReducer。

useState 本質(zhì)上在使用 useReducer,在 React 源碼層提供了特殊的名為 basicStateReducer 的 reducer,后面源碼解析中會(huì)看到它。

const _useState = (initalVal) => {
return React.useReducer(
function (preState, action) {
// action 對(duì)應(yīng) setState 傳入的最新狀態(tài)
// 如果不是函數(shù),直接更新為最新狀態(tài)
// 如果是函數(shù),傳入 preState 并調(diào)用函數(shù),并將返回值作為最新狀態(tài)
return typeof action === 'function' ? action(preState) : action;
},
initalVal
)
}

回到正題。

useState 在更新階段會(huì)拿到上一次的狀態(tài)值,此階段調(diào)用的是 HooksDispatcherOnUpdate.useState,也就是 updateState。

updateState 會(huì)調(diào)用 updateReducer(useReducer 更新階段也用這個(gè)),這也是為什么我說 setState 是特殊 useReducer 的原因。

updateReducer 主要工作有兩個(gè):

  1. 從 current.memorizedState 拷貝 hook 到 workInProcess 下(updateWorkInProgressHook 方法)。
  2. 將 hook.queue.pending 隊(duì)列合并到 currentHook.baseQueue 下,然后遍歷隊(duì)列中的 update 對(duì)象,使用 action 和 reducer 計(jì)算出最新的狀態(tài),更新到 hook 上,最后返回新狀態(tài)和新 setState。
function updateState(initialState) {
// 實(shí)際用的是 updateReducer
return updateReducer(basicStateReducer);
}
// reducer 函數(shù)
function basicStateReducer(state, action) {
return typeof action === 'function' ? action(state) : action;
}

// setReducer 更新階段對(duì)應(yīng)的 updateReducer
function updateReducer(reducer, initialArg, init) {
// ----- 【1】 拷貝 hook(current -> workInProcess),并返回這個(gè) hook -----
const hook = updateWorkInProgressHook();

// ----- 【2】 讀取隊(duì)列,計(jì)算出最新狀態(tài),更新 hook 的狀態(tài) -----
// ...
}

先看看 updateWorkInProgressHook 方法。

該方法中,currentHook 設(shè)置為 current.memoizedState 鏈表的下一個(gè) hook,拷貝它到 currentlyRenderingFiber.memoizedState 鏈表上,返回這個(gè) hook。

function updateWorkInProgressHook() {
// 1. 移動(dòng) currentHook 指針
//(來自 current.memoizedState 鏈表)
var nextCurrentHook;
if (currentHook === null) {
var current = currentlyRenderingFiber.alternate;
if (current !== null) {
nextCurrentHook = current.memoizedState;
} else {
nextCurrentHook = null;
}
} else {
nextCurrentHook = currentHook.next;
}

// 2. 移動(dòng) workInProgressHook 指針
//(來自 currentlyRenderingFiber.memoizedState 鏈表)
var nextWorkInProgressHook;
if (workInProgressHook === null) {
nextWorkInProgressHook = currentlyRenderingFiber.memoizedState;
} else {
nextWorkInProgressHook = workInProgressHook.next;
}

if (nextWorkInProgressHook !== null) {
// 這種情況為 “渲染時(shí)更新邏輯”(在 render 時(shí)調(diào)用了 setState)
// 為了更聚焦普通情況,這里不討論
workInProgressHook = nextWorkInProgressHook;
nextWorkInProgressHook = workInProgressHook.next;
currentHook = nextCurrentHook;
} else {
// 3. 渲染時(shí)不更新,nextWorkInProgressHook 就一定是 null
if (nextCurrentHook === null) {
throw new Error('Rendered more hooks than during the previous render.');
}

currentHook = nextCurrentHook;
var newHook = {
memoizedState: currentHook.memoizedState,
baseState: currentHook.baseState,
baseQueue: currentHook.baseQueue,
queue: currentHook.queue,
next: null // next 就不拷貝了
};

// 4. 經(jīng)典單鏈表末尾加節(jié)點(diǎn)寫法
if (workInProgressHook === null) {
currentlyRenderingFiber.memoizedState = workInProgressHook = newHook;
} else {
workInProgressHook = workInProgressHook.next = newHook;
}
}

// 5. 返回拷貝 hook 對(duì)象
return workInProgressHook;
}

拿到拷貝后的 hook,就可以計(jì)算新狀態(tài)值了。

首先將 hook.queue.pending 隊(duì)列合并到 currentHook.baseQueue 下。該隊(duì)列包含了一系列 update 對(duì)象(因?yàn)榭赡苷{(diào)用了多次 setState),里面保存有 setState 傳入的最新狀態(tài)值(函數(shù)或其他值)。

然后遍歷 update 計(jì)算出最新狀態(tài),保存回 hook,并返回最新狀態(tài)值和 setState 方法。

function updateReducer(reducer, initialArg, init) {
// ----- 【1】 拷貝 hook(current -> workInProcess),并返回這個(gè) hook ----
const hook = updateWorkInProgressHook();

// ----- 【2】 讀取隊(duì)列,計(jì)算出最新狀態(tài),更新 hook 的狀態(tài) -----
// 取出 hook.queue 鏈表,添加到 current.baseQueue 末尾
const queue = hook.queue;
queue.lastRenderedReducer = reducer;
const current = currentHook;
let baseQueue = current.baseQueue;
const pendingQueue = queue.pending;
if (pendingQueue !== null) {
if (baseQueue !== null) {
const baseFirst = baseQueue.next;
const pendingFirst = pendingQueue.next;
baseQueue.next = pendingFirst;
pendingQueue.next = baseFirst;
}
current.baseQueue = baseQueue = pendingQueue;
queue.pending = null;
}

// 處理更新隊(duì)列
if (baseQueue !== null) {
const first = baseQueue.next;
let newState = current.baseState;

let newBaseState = null;
let newBaseQueueFirst = null;
let newBaseQueueLast = null;
let update = first;

// 循環(huán),根據(jù) baseQueue 鏈表下的 update 對(duì)象計(jì)算新狀態(tài)
do {
// 刪掉了一些跳過更新的邏輯

if (update.hasEagerState) {
// 為了對(duì)比新舊狀態(tài)來決定是否更新,所計(jì)算的新狀態(tài)。
// 如果不同,給 update.hasEagerState 設(shè)置為 true
// 新狀態(tài)賦值給 update.eagerState
newState = update.eagerState;
} else {
// 計(jì)算新狀態(tài)
const action = update.action;
newState = reducer(newState, action);
}
update = update.next;
} while (update !== null && update !== first);
if (newBaseQueueLast === null) {
newBaseState = newState;
} else {
newBaseQueueLast.next = newBaseQueueFirst;
}
if (!is(newState, hook.memoizedState)) {
markWorkInProgressReceivedUpdate();
}
// 更新 hook 狀態(tài)
hook.memoizedState = newState;
hook.baseState = newBaseState;
hook.baseQueue = newBaseQueueLast;
queue.lastRenderedState = newState;
}
const dispatch = queue.dispatch;
return [hook.memoizedState, dispatch];
}

useEffect

有些邏輯類似 useState,比如創(chuàng)建 hook 的 mountWorkInProgressHook 方法實(shí)現(xiàn),所以一些重復(fù)邏輯就不說了,直奔核心。

掛載階段

核心函數(shù)是 mountEffectImpl。

  1. 【mountWorkInProgressHook】創(chuàng)建一個(gè) hook 空對(duì)象,放到 workInProcess.memorizedState 下。
  2. 【pushEffect】創(chuàng)建 effect,添加到 當(dāng)前 fiber 的 updateQueue 的鏈表上,并將該 effect 賦值給 hook.memoizedState。
mountEffectImpl(fiberFlags, hookFlags, create, deps) {
// create 和 deps 是 useEffect 接受的兩個(gè)參數(shù)

// 1. 新建 hook 對(duì)象
const hook = mountWorkInProgressHook();

const nextDeps = deps === undefined ? null : deps;
currentlyRenderingFiber.flags |= fiberFlags;

// 2. 新建 effect 對(duì)象,放到 hook.memoizedState 下。
hook.memoizedState = pushEffect(
HookHasEffect | hookFlags,
create,
undefined,
nextDeps,
);
}

pushEffect 實(shí)現(xiàn):

function pushEffect(tag, create, destroy, deps) {
// 創(chuàng)建 effect
var effect = {
tag: tag,
create: create,
destroy: destroy,
deps: deps,
next: null
};
var componentUpdateQueue = currentlyRenderingFiber.updateQueue;

// 添加到當(dāng)前 fiber.updateQueue 下。
// updateQueue.laseEffect 保存鏈表的最后一個(gè) effect
// 且使用的是環(huán)形鏈表,通過 updateQueue.laseEffect.next 得到鏈表頭節(jié)點(diǎn)

// 如果 updateQueue 為 null,初始化一個(gè)空的 updateQueue 對(duì)象
if (componentUpdateQueue === null) {
componentUpdateQueue = createFunctionComponentUpdateQueue();
currentlyRenderingFiber.updateQueue = componentUpdateQueue;
componentUpdateQueue.lastEffect = effect.next = effect;
} else {
// 往 updateQueue.lastEffect 鏈表上添加 effect 對(duì)象。
var lastEffect = componentUpdateQueue.lastEffect;

if (lastEffect === null) {
componentUpdateQueue.lastEffect = effect.next = effect;
} else {
var firstEffect = lastEffect.next;
lastEffect.next = effect;
effect.next = firstEffect;
componentUpdateQueue.lastEffect = effect;
}
}

return effect;
}

更新階段

核心實(shí)現(xiàn)在 updateEffectImpl。

  1. 從 current 拷貝 hook 到 workInProcess。
  2. 對(duì)比新舊依賴項(xiàng) deps,如果沒改變,也創(chuàng)建 effect 加隊(duì)列上(但最終不會(huì)執(zhí)行),結(jié)束;否則繼續(xù)。
  3. 給當(dāng)前 fiber 打上 PassiveEffect,表示有 useEffect 的回調(diào)要執(zhí)行。
  4. 創(chuàng)建 effect ,tag 補(bǔ)上加 HookHasEffect,然后加隊(duì)列上,后面會(huì)執(zhí)行。
function updateEffect(create, dep) {
return updateEffectImpl(PassiveEffect, HookPassive, create, deps);
}

function updateEffectImpl(fiberFlags, hookFlags, create, deps): void {
// hookFlags 此時(shí)為 PassiveEffect(代表)

// 1. 從 current 拷貝 hook 到 workInProcess
const hook = updateWorkInProgressHook();

const nextDeps = deps === undefined ? null : deps;
let destroy = undefined;

if (currentHook !== null) {
const prevEffect = currentHook.memoizedState;
destroy = prevEffect.destroy;
// 存在依賴項(xiàng)
if (nextDeps !== null) {
const prevDeps = prevEffect.deps;
// 依賴項(xiàng)沒有改變,結(jié)束
if (areHookInputsEqual(nextDeps, prevDeps)) {
// 還是會(huì)新建 effect,更新 updateQueue 和 memorizedState
// 但 tag 只是 PassiveEffect,后面遍歷時(shí)不會(huì)執(zhí)行
hook.memoizedState = pushEffect(hookFlags, create, destroy, nextDeps);
return;
}
}
}

// 當(dāng)前 fiber 打上 PassiveEffect 標(biāo)記
// 該標(biāo)記表示存在需要執(zhí)行的 useEffect
currentlyRenderingFiber.flags |= fiberFlags;

hook.memoizedState = pushEffect(
// 相比上面依賴項(xiàng)不變的情況,這里加了 HookHasEffect 標(biāo)簽
// 之后根據(jù) fiber.updateQueue 會(huì)執(zhí)行
HookHasEffect | hookFlags,
create,
destroy,
nextDeps,
);
}

我們看下依賴項(xiàng)對(duì)比算法 areHookInputsEqual 的細(xì)節(jié),它同時(shí)遍歷到新舊依賴項(xiàng)最長的尾部,進(jìn)行 Object.is 對(duì)比。在空數(shù)組情況下,這個(gè)比較一定返回 true,所以能模擬 componentDidMount / Unmount 的效果。

function areHookInputsEqual(nextDeps, prevDeps) {
for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
if (is(nextDeps[i], prevDeps[i])) {
continue;
}
return false;
}
return true;
}

useEffect 的 create 和 destroy 的執(zhí)行時(shí)機(jī)

當(dāng) commit 階段結(jié)束后,useEffect 的 create 和 destroy 會(huì)被 Schedule 調(diào)度器異步調(diào)度執(zhí)行。

fiber.updateQueue 下的 effect 會(huì)按順序取出,然后一個(gè)個(gè)執(zhí)行。

function commitPassiveUnmountOnFiber(finishedWork) {
// 執(zhí)行所有 tag 為 HookPassive | HookHasEffect 的 effect 的 destroy
commitHookEffectListUnmount(
HookPassive | HookHasEffect,
finishedWork,
finishedWork.return,
);
// 執(zhí)行所有 tag 為 HookPassive | HookHasEffect 的 effect 的 create
commitHookEffectListUnmount(
HookPassive | HookHasEffect,
finishedWork,
finishedWork.return,
);
}

之前依賴項(xiàng)相同的話,雖然也創(chuàng)建 effect,但它的 tag 對(duì)不上,是不會(huì)執(zhí)行的。

一些面試題的簡單回答

1、React Hooks 為什么不能寫在條件語句中?

我們要保證 React Hooks 的順序一致。

函數(shù)組件的狀態(tài)是保存在 fiber.memorizedState 中的。它是一個(gè)鏈表,保存調(diào)用 Hook 生成的 hook 對(duì)象,這些對(duì)象保存著狀態(tài)值。當(dāng)更新時(shí),我們每調(diào)用一個(gè) Hook,其實(shí)就是從 fiber.memorizedState 鏈表中讀取下一個(gè) hook,取出它的狀態(tài)。

如果順序不一致了或者數(shù)量不一致了,就會(huì)導(dǎo)致錯(cuò)誤,取出了一個(gè)其他 Hook 對(duì)應(yīng)的狀態(tài)值。

2、React Hooks 為什么必須在函數(shù)組件內(nèi)部執(zhí)行?React 如何能夠監(jiān)聽 React Hooks 在外部執(zhí)行并拋出異常??

Hooks 底層調(diào)用的是一個(gè)全局變量 ReactCurrentDispatcher 的一系列方法。

這個(gè)全局變量會(huì)在不同階段設(shè)置為不同的對(duì)象。render 過程中,掛載階段設(shè)置為 HooksDispatcherOnMount,更新階段設(shè)置為 HooksDispatcherOnUpdate。它們會(huì)讀取 currentlyRenderingFiber 全局變量,這個(gè)全局變量代表正在處理的 fiber,讀取它進(jìn)行一些設(shè)置狀態(tài)和讀取狀態(tài)等操作。

在 render 階段外,會(huì)設(shè)置為 ContextOnlyDispatcher,這個(gè)對(duì)象下所有方法都會(huì)拋出錯(cuò)誤,因?yàn)榇藭r(shí)不存在正常處理的 fiber,使用時(shí)機(jī)是并不對(duì)。

結(jié)尾

本文只講了狀態(tài) Hook 代表 useState,和 副作用 Hook 代表 useEffect,其他 Hook 其實(shí)也差不多。

責(zé)任編輯:姜華 來源: 前端西瓜哥
相關(guān)推薦

2022-12-07 11:21:30

Reactdiff

2022-04-14 09:01:39

React源碼Flow

2016-10-26 20:49:24

ReactJavascript前端

2019-04-11 15:45:08

ReactMixin前端

2022-04-14 11:50:39

函數(shù)組件hook

2024-09-05 08:04:16

ReactuseState()工具

2022-05-04 10:38:58

React閉包組件

2021-05-10 17:20:55

AIOps開發(fā)人員人工智能

2019-02-18 14:42:18

React.jsUI前端

2022-05-05 08:31:48

useRefuseEffecthook

2011-08-08 13:45:58

jQuery

2020-12-20 10:02:17

ContextReactrender

2020-05-28 13:33:30

React Hook前端開發(fā)

2024-07-16 09:51:39

HTMLHookReact

2024-09-06 17:55:27

Springboot開發(fā)

2024-01-16 08:43:51

React底層機(jī)制Hook

2022-03-27 09:06:04

React類型定義前端

2023-03-06 00:27:02

Kubernetesscheduler系統(tǒng)

2021-05-14 09:49:47

React HookReact應(yīng)用

2022-08-01 07:56:23

React Hook開發(fā)組件
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 成人在线一区二区 | 久久99精品久久久久蜜桃tv | 中文字幕一区二区三区日韩精品 | 9191在线播放 | 粉色午夜视频 | 日韩精品在线一区二区 | 日韩中文欧美 | 中文字幕在线观看成人 | 日韩日韩日韩日韩日韩日韩日韩 | 国产精品国产精品国产专区不卡 | 一级黄色片在线免费观看 | 伊人伊成久久人综合网站 | 日韩一区二区在线观看 | 精品久久九九 | 久久最新精品 | 精品国产乱码一区二区三区 | 中文字幕日韩三级 | 国产精品久久久久久久久久久免费看 | 国产伦精品一区二区三区高清 | 国产婷婷色一区二区三区 | 亚洲精品一区在线 | 成人久久久久久久久 | 九九综合| 欧洲妇女成人淫片aaa视频 | av毛片 | 国产在线一区二区三区 | 曰批视频在线观看 | 国产成人av电影 | 亚洲一区视频在线播放 | 国产精品精品久久久 | 国产免费观看久久黄av片涩av | 97精品超碰一区二区三区 | 在线中文字幕第一页 | 99re在线视频 | 国产精品久久久久久中文字 | 狠狠爱综合 | 伦理二区 | 亚洲精品美女在线观看 | 一色桃子av一区二区 | 欧美成视频在线观看 | 91亚洲欧美 |