從本篇開始,咱們講 React-Hooks 最經常使用的幾個函數,先經過例子來看下React.useState()
:javascript
import React, {useEffect} from 'react';
import React from 'react';
function App() {
debugger
const [name, setName] = React.useState( 'chen');
return (
<div onClick={()=>setName('jin')}>
{name}
</div>
);
}
export default App;
複製代碼
當執行App()
時,會調用React.useState('chen')
,由於是useState()
的第一次調用,因此此時就會執行源碼裏的mountState()
php
初始化useState()
,並返回一個數組html
//第一次更新 state 走這裏
//useState的核心源碼
//initialState 就是 React.useState(initialState) 設的初始值
function mountState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const hook = mountWorkInProgressHook();
//若是 initValue 是 function 的話,則獲取執行的結果
if (typeof initialState === 'function') {
initialState = initialState();
}
hook.memoizedState = hook.baseState = initialState;
//注意下這邊的語法,連等賦值,等同於:
//hook.queue = { xxx }
//const queue = hook.queue
const queue = (hook.queue = {
last: null,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: (initialState: any),
});
//這邊又是一個連等賦值
const dispatch: Dispatch<
BasicStateAction<S>,
> = (queue.dispatch = (dispatchAction.bind(
//注意,由於 FunctionComponent 沒有 this,因此 bind()第一個參數是 null
null,
// Flow doesn't know this is non-null, but we do.
//當前正要渲染的 fiber 對象
((currentlyRenderingFiber: any): Fiber),
queue,
): any));
//initialState,dispatchAction.bind(null,currentlyRenderingFiber,queue,)
//開發層面:const [name,setName]=React.useState('chen')
//那麼 name就是hook.memoizedState,賦值了'chen'
//setName 就是 dispatch,即 dispatchAction.bind(null,currentlyRenderingFiber,queue,)
return [hook.memoizedState, dispatch];
}
複製代碼
(1) 執行mountWorkInProgressHook()
,獲取最新的hook 鏈表java
mountWorkInProgressHook()
源碼以下:react
//將當前 hook 加入 workInProgressHook 鏈表中,
//並返回最新的 hook 鏈表
function mountWorkInProgressHook(): Hook {
const hook: Hook = {
memoizedState: null,
baseState: null,
queue: null,
baseUpdate: null,
next: null,
};
if (workInProgressHook === null) {
// This is the first hook in the list
firstWorkInProgressHook = workInProgressHook = hook;
} else {
// Append to the end of the list
workInProgressHook = workInProgressHook.next = hook;
}
//獲取最新的 hook 鏈表,並返回
return workInProgressHook;
}
複製代碼
(2) 若是initialState
是一個callback
的話,好比:git
const [styleObj, setStyleObj] = React.useState(()=>{return {height:14,} });
複製代碼
那麼就會去執行initialState
:github
//若是 initValue 是 function 的話,則獲取執行的結果
if (typeof initialState === 'function') {
initialState = initialState();
}
複製代碼
(3) 而後初始化了hook.memoizedState
、hook.baseState
、hook.queue
和queue.dispatch
web
(4) basicStateReducer
的源碼以下:sql
//傳入參數 A、B,若是 B 是 function,則返回 B(A),不然返回 B
function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
return typeof action === 'function' ? action(state) : action;
}
複製代碼
(5) 注意下dispatchAction()
的使用:json
dispatchAction.bind(
//注意,由於 FunctionComponent 沒有 this,因此 bind()第一個參數是 null
null,
// Flow doesn't know this is non-null, but we do.
//當前正要渲染的 fiber 對象
((currentlyRenderingFiber: any): Fiber),
queue,
)
複製代碼
由於 React-Hooks 只適用於FunctionComponent
,因此綁定了this
爲null
,而且傳入了參數——currentlyRenderingFiber
、queue
(6) 最終返回一個數組:
[hook.memoizedState, dispatch]
複製代碼
在開發層面chen
賦值給了hook.memoizedState
setName
表示dispatch
,注意,這裏 setName 只是形參,並無賦值給 dispatch
(7) 此時的name 爲'chen',當點擊 div,調用setName('jin')
時,會執行dispatchAction('jin')
函數,咱們來看下dispatchAction
源碼
(1) 新建 update 對象:{action:'jin'}
(2) 將 update 加至 hook.queue 的末尾:hook.queue.last = update
(3) 執行 scheduleWork(),走 updateFunctionComponent()
流程
//一、新建 update 對象:{action:'jin'}
//二、將 update 加至 hook.queue 的末尾:hook.queue.last = update
//三、執行 scheduleWork(),走 updateFunctionComponent() 流程
function dispatchAction<S, A>(
fiber: Fiber, //當前正要渲染的 fiber 對象
queue: UpdateQueue<S, A>,
action: A, //'jin'
) {
invariant(
numberOfReRenders < RE_RENDER_LIMIT,
'Too many re-renders. React limits the number of renders to prevent ' +
'an infinite loop.',
);
//刪除了 dev 代碼
const alternate = fiber.alternate;
//因爲這邊的currentlyRenderingFiber爲 null,傳進來的fiber(currentlyRenderingFiber)有值,因此走 else 狀況
if (
fiber === currentlyRenderingFiber ||
(alternate !== null && alternate === currentlyRenderingFiber)
) {
// This is a render phase update. Stash it in a lazily-created map of
// queue -> linked list of updates. After this render pass, we'll restart
// and apply the stashed updates on top of the work-in-progress hook.
didScheduleRenderPhaseUpdate = true;
const update: Update<S, A> = {
expirationTime: renderExpirationTime,
suspenseConfig: null,
action,
eagerReducer: null,
eagerState: null,
next: null,
};
if (renderPhaseUpdates === null) {
renderPhaseUpdates = new Map();
}
const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
//獲取update,沒有則初始化
if (firstRenderPhaseUpdate === undefined) {
renderPhaseUpdates.set(queue, update);
}
//若是有 update 的話,則遍歷至 update 隊尾,將最新的 update 添加至隊尾
else {
// Append the update to the end of the list.
let lastRenderPhaseUpdate = firstRenderPhaseUpdate;
while (lastRenderPhaseUpdate.next !== null) {
lastRenderPhaseUpdate = lastRenderPhaseUpdate.next;
}
lastRenderPhaseUpdate.next = update;
}
}
else {
if (revertPassiveEffectsChange) {
flushPassiveEffects();
}
const currentTime = requestCurrentTime();
const suspenseConfig = requestCurrentSuspenseConfig();
const expirationTime = computeExpirationForFiber(
currentTime,
fiber,
suspenseConfig,
);
//新建 update 對象
//注意 action 就是傳進來要更新的 state->'jin'
const update: Update<S, A> = {
expirationTime,
suspenseConfig,
action,
eagerReducer: null,
eagerState: null,
next: null,
};
// Append the update to the end of the list.
//將 update 加至 hook.queue 的末尾
const last = queue.last;
if (last === null) {
// This is the first update. Create a circular list.
update.next = update;
} else {
const first = last.next;
if (first !== null) {
// Still circular.
update.next = first;
}
last.next = update;
}
queue.last = update;
//fiber的優先級expirationTime爲 0,和NoWork值相等,
//而且alternate(也就是fiber的副本)的expirationTime也爲 0,因此條件成立
if (
fiber.expirationTime === NoWork &&
(alternate === null || alternate.expirationTime === NoWork)
) {
// The queue is currently empty, which means we can eagerly compute the
// next state before entering the render phase. If the new state is the
// same as the current state, we may be able to bail out entirely.
const lastRenderedReducer = queue.lastRenderedReducer;
if (lastRenderedReducer !== null) {
let prevDispatcher;
//刪除了 dev 代碼
try {
const currentState: S = (queue.lastRenderedState: any);
const eagerState = lastRenderedReducer(currentState, action);
// Stash the eagerly computed state, and the reducer used to compute
// it, on the update object. If the reducer hasn't changed by the
// time we enter the render phase, then the eager state can be used
// without calling the reducer again.
//queue.last 也會同步更新,由於是同一引用地址
update.eagerReducer = lastRenderedReducer;
// update.eagerState = 'jin'
update.eagerState = eagerState;
if (is(eagerState, currentState)) {
// Fast path. We can bail out without scheduling React to re-render.
// It's still possible that we'll need to rebase this update later,
// if the component re-renders for a different reason and by that
// time the reducer has changed.
return;
}
} catch (error) {
// Suppress the error. It will throw again in the render phase.
} finally {
if (__DEV__) {
ReactCurrentDispatcher.current = prevDispatcher;
}
}
}
}
//刪除了 dev 代碼
//最後執行scheduleWork(),以後會到 updateFunctionComponent 那邊
//關於 scheduleWork 的講解,請看:
//[React源碼解析之scheduleWork(上)](https://juejin.im/post/5d7fa983f265da03cf7ac048)
//[React源碼解析之scheduleWork(下)](https://juejin.im/post/5d885b75f265da03e83baaa7)
scheduleWork(fiber, expirationTime);
}
}
複製代碼
(1) 首先注意下傳進來的fiber
和這裏面的currentlyRenderingFiber
不是同一個fiber
此時currentlyRenderingFiber
爲 null,由於尚未進行FunctionComponent
的更新
但傳進來的fiber
是有值的,也就是App()
(2) 因此,如下條件不成立:
//因爲這邊的currentlyRenderingFiber爲 null,傳進來的fiber(currentlyRenderingFiber)有值,因此走 else 狀況
if (
fiber === currentlyRenderingFiber ||
(alternate !== null && alternate === currentlyRenderingFiber)
) {
}
複製代碼
看下 else 狀況
(3) 新建 update 對象:
const update: Update<S, A> = {
expirationTime,
suspenseConfig,
action,
eagerReducer: null,
eagerState: null,
next: null,
};
複製代碼
注意:action
就是傳進來要更新的state
—>'jin'
(4) 將update
對象加至hook.queue
的末尾
//將 update 加至 hook.queue 的末尾
const last = queue.last;
if (last === null) {
// This is the first update. Create a circular list.
update.next = update;
} else {
const first = last.next;
if (first !== null) {
// Still circular.
update.next = first;
}
last.next = update;
}
queue.last = update;
複製代碼
(5) fiber
的優先級expirationTime
爲 0,和NoWork
值相等,而且alternate
(也就是fiber
的副本)的expirationTime
也爲 0,因此條件成立
if (
fiber.expirationTime === NoWork &&
(alternate === null || alternate.expirationTime === NoWork)
) {
}
複製代碼
(6) lastRenderedReducer
在1、mountState()
中「解析」的(4)
提到過,是有值的,因此走這邊:
if (lastRenderedReducer !== null) {
}
複製代碼
(7) 如下代碼的目的是將'jin'
賦值到update.eagerState
中,方便以後用到:
const currentState: S = (queue.lastRenderedState: any);
const eagerState = lastRenderedReducer(currentState, action);
//queue.last 也會同步更新,由於是同一引用地址
update.eagerReducer = lastRenderedReducer;
// update.eagerState = 'jin'
update.eagerState = eagerState;
複製代碼
(8) 最後執行scheduleWork()
方法,爲何以後會到updateFunctionComponent
那邊,能夠自行斷點調試查看:
(9) 關於scheduleWork()
的講解,請看:
React源碼解析之scheduleWork(上)
React源碼解析之scheduleWork(下)
(1) updateFunctionComponent()
源碼中,有一個renderWithHooks()
方法:
//在渲染的過程當中,對裏面用到的 hooks 函數作一些操做
nextChildren = renderWithHooks(
current,
workInProgress,
Component,
nextProps,
context,
renderExpirationTime,
);
複製代碼
(2) renderWithHooks()
中有一個Component()
方法:
//workInProgress.type,這裏能當作 function 使用,說明 type 是 function
//App()
let children = Component(props, refOrContext);
複製代碼
注意:
這裏的Component
,是workInProgress.type
,也就是App
方法:
function App() {
debugger
const [name, setName] = React.useState( 'chen');
return (
<div onClick={()=>setName('jin')}>
{name}
</div>
);
}
複製代碼
執行App()
後,又會到const [name, setName] = React.useState( 'chen');
上,但此時的useState
調用的不是源碼中的mountState()
,而是updateState('chen')
!!
屢次更新initialState
的值
//屢次更新 state 走這裏
function updateState<S>(
initialState: (() => S) | S, //'chen'
): [S, Dispatch<BasicStateAction<S>>] {
//basicStateReducer 源碼:action === 'function' ? action(state) : action;
return updateReducer(basicStateReducer, (initialState: any));
}
複製代碼
(1) 能夠看到,此時的updateState
,實際上是調用的updateReducer
,
也就是說:
此時,useState()
的本質是useReducer()
關於useReducer()
的做用及使用,請看:
zh-hans.reactjs.org/docs/hooks-…
(2) 注意此時的initialState
仍然是'chen'
屢次更新initialState
的值
//useState 屢次更新時調用 updateReducer
//reducer:basicStateReducer
//initialArg:initialState
function updateReducer<S, I, A>(
reducer: (S, A) => S,
initialArg: I,
init?: I => S,
): [S, Dispatch<A>] {
//當前正在 update 的 hook
//{
// memoizedState: "chen"
// baseState: "chen"
// queue:{
// last:{
// expirationTime: 1073741823
// action: "jin",
// eagerState: "jin"
// next:是 last 對象,因此 queue 是單向鏈表
// }
// }
//}
const hook = updateWorkInProgressHook();
//hook 的更新隊列
const queue = hook.queue;
invariant(
queue !== null,
'Should have a queue. This is likely a bug in React. Please file an issue.',
);
//()=>{ return typeof action === 'function' ? action(state) : action;}
queue.lastRenderedReducer = reducer;
//numberOfReRenders=0,下面不看
if (numberOfReRenders > 0) {
// This is a re-render. Apply the new render phase updates to the previous
// work-in-progress hook.
const dispatch: Dispatch<A> = (queue.dispatch: any);
if (renderPhaseUpdates !== null) {
// Render phase updates are stored in a map of queue -> linked list
const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
if (firstRenderPhaseUpdate !== undefined) {
renderPhaseUpdates.delete(queue);
let newState = hook.memoizedState;
let update = firstRenderPhaseUpdate;
do {
// Process this render phase update. We don't have to check the
// priority because it will always be the same as the current
// render's.
const action = update.action;
newState = reducer(newState, action);
update = update.next;
} while (update !== null);
// Mark that the fiber performed work, but only if the new state is
// different from the current state.
if (!is(newState, hook.memoizedState)) {
markWorkInProgressReceivedUpdate();
}
hook.memoizedState = newState;
// Don't persist the state accumlated from the render phase updates to
// the base state unless the queue is empty.
// TODO: Not sure if this is the desired semantics, but it's what we
// do for gDSFP. I can't remember why.
if (hook.baseUpdate === queue.last) {
hook.baseState = newState;
}
queue.lastRenderedState = newState;
return [newState, dispatch];
}
}
return [hook.memoizedState, dispatch];
}
// The last update in the entire queue
//獲取hook 更新隊列上最新的 update 對象
const last = queue.last;
// The last update that is part of the base state.
const baseUpdate = hook.baseUpdate; //baseUpdate = null
const baseState = hook.baseState; //baseState = "chen"
// Find the first unprocessed update.
let first;
//baseUpdate = null
if (baseUpdate !== null) {
if (last !== null) {
// For the first update, the queue is a circular linked list where
// `queue.last.next = queue.first`. Once the first update commits, and
// the `baseUpdate` is no longer empty, we can unravel the list.
last.next = null;
}
first = baseUpdate.next;
} else {
first = last !== null ? last.next : null;
}
//first:{
// expirationTime: 1073741823
// action: "jin",
// eagerState: "jin"
// next:first 對象
// }
if (first !== null) {
let newState = baseState; //baseState = "chen"
let newBaseState = null;
let newBaseUpdate = null;
let prevUpdate = baseUpdate; //baseUpdate = null
let update = first; //object
let didSkip = false;
do {
const updateExpirationTime = update.expirationTime; //1073741823
//二者相等,跳過
if (updateExpirationTime < renderExpirationTime) {
// Priority is insufficient. Skip this update. If this is the first
// skipped update, the previous update/state is the new base
// update/state.
if (!didSkip) {
didSkip = true;
newBaseUpdate = prevUpdate;
newBaseState = newState;
}
// Update the remaining priority in the queue.
if (updateExpirationTime > remainingExpirationTime) {
remainingExpirationTime = updateExpirationTime;
}
}
//走這裏
else {
// This update does have sufficient priority.
// Mark the event time of this update as relevant to this render pass.
// TODO: This should ideally use the true event time of this update rather than
// its priority which is a derived and not reverseable value.
// TODO: We should skip this update if it was already committed but currently
// we have no way of detecting the difference between a committed and suspended
// update here.
markRenderEventTimeAndConfig(
updateExpirationTime,
update.suspenseConfig,
);
// Process this update.
// update.eagerReducer : basicStateReducer function
// reducer : basicStateReducer function
//二者相同,走這裏
//獲取 update 的 state 值,新值
if (update.eagerReducer === reducer) {
// If this update was processed eagerly, and its reducer matches the
// current reducer, we can use the eagerly computed state.
newState = ((update.eagerState: any): S); //eagerState: "jin"
} else {
const action = update.action;
newState = reducer(newState, action);
}
}
prevUpdate = update;
update = update.next;
//update.next 就是 last 對象,last 對象就是 first,二者相等,跳出循環
} while (update !== null && update !== first);
if (!didSkip) {
newBaseUpdate = prevUpdate;
newBaseState = newState;
}
// Mark that the fiber performed work, but only if the new state is
// different from the current state.
//判斷 memoizedState(oldState) 和 newState 是否真的不一樣
if (!is(newState, hook.memoizedState)) {
//標記當前 fiber 的確收到了 update
markWorkInProgressReceivedUpdate();
}
hook.memoizedState = newState; //newState = "jin"
hook.baseUpdate = newBaseUpdate;
hook.baseState = newBaseState; //newBaseState = "jin"
queue.lastRenderedState = newState;
}
const dispatch: Dispatch<A> = (queue.dispatch: any);
//[name,setName]
//dispatchAction
return [hook.memoizedState, dispatch];
}
複製代碼
(1) 執行updateWorkInProgressHook()
,獲取當前正在update
的hook
:
const hook = updateWorkInProgressHook();
複製代碼
簡單看下updateWorkInProgressHook()
的源碼:
//當前正在 update 的 fiber 上的 hook
function updateWorkInProgressHook(): Hook {
// This function is used both for updates and for re-renders triggered by a
// render phase update. It assumes there is either a current hook we can
// clone, or a work-in-progress hook from a previous render pass that we can
// use as a base. When we reach the end of the base list, we must switch to
// the dispatcher used for mounts.
if (nextWorkInProgressHook !== null) {
// There's already a work-in-progress. Reuse it.
workInProgressHook = nextWorkInProgressHook;
nextWorkInProgressHook = workInProgressHook.next;
currentHook = nextCurrentHook;
nextCurrentHook = currentHook !== null ? currentHook.next : null;
} else {
// Clone from the current hook.
invariant(
nextCurrentHook !== null,
'Rendered more hooks than during the previous render.',
);
currentHook = nextCurrentHook;
const newHook: Hook = {
memoizedState: currentHook.memoizedState,
baseState: currentHook.baseState,
queue: currentHook.queue,
baseUpdate: currentHook.baseUpdate,
next: null,
};
if (workInProgressHook === null) {
// This is the first hook in the list.
workInProgressHook = firstWorkInProgressHook = newHook;
} else {
// Append to the end of the list.
workInProgressHook = workInProgressHook.next = newHook;
}
nextCurrentHook = currentHook.next;
}
return workInProgressHook;
}
複製代碼
最後返回的workInProgressHook
,大概是這樣:
const workInProgressHook={
memoizedState: "chen",
baseState: "chen",
queue:{
last:{
expirationTime: 1073741823,
action: "jin",
eagerState: "jin",
next: //是 last 對象,因此 queue 是單向鏈表
}
}
}
複製代碼
(2) hook.queue
是hook
的更新隊列,咱們須要更新的值就在queue.last. eagerState/action
中:
const queue = hook.queue;
複製代碼
(3) numberOfReRenders
表示從新渲染時fiber
的節點數,也就是在render 的時候,又產生了 update 時,會加 1,但這裏的numberOfReRenders
爲 0,因此不走這邊:
if (numberOfReRenders > 0) {
}
複製代碼
(4) 賦值first
爲hook.queue.last.next
:
// Find the first unprocessed update.
let first;
//baseUpdate = null
if (baseUpdate !== null) {
if (last !== null) {
// For the first update, the queue is a circular linked list where
// `queue.last.next = queue.first`. Once the first update commits, and
// the `baseUpdate` is no longer empty, we can unravel the list.
last.next = null;
}
first = baseUpdate.next;
} else {
first = last !== null ? last.next : null;
}
複製代碼
first
大概長這樣:
const first={
expirationTime: 1073741823,
action: "jin",
eagerState: "jin",
next: //仍是first 對象
}
複製代碼
(5) 由於first
不爲 null,因此走這邊
if (first !== null) {
let newState = baseState; //baseState = "chen"
let newBaseState = null;
let newBaseUpdate = null;
let prevUpdate = baseUpdate; //baseUpdate = null
let update = first; //object
let didSkip = false;
do {
const updateExpirationTime = update.expirationTime; //1073741823
//二者相等,跳過
if (updateExpirationTime < renderExpirationTime) {
// Priority is insufficient. Skip this update. If this is the first
// skipped update, the previous update/state is the new base
// update/state.
if (!didSkip) {
didSkip = true;
newBaseUpdate = prevUpdate;
newBaseState = newState;
}
// Update the remaining priority in the queue.
if (updateExpirationTime > remainingExpirationTime) {
remainingExpirationTime = updateExpirationTime;
}
}
//走這裏
else {
// This update does have sufficient priority.
// Mark the event time of this update as relevant to this render pass.
// TODO: This should ideally use the true event time of this update rather than
// its priority which is a derived and not reverseable value.
// TODO: We should skip this update if it was already committed but currently
// we have no way of detecting the difference between a committed and suspended
// update here.
markRenderEventTimeAndConfig(
updateExpirationTime,
update.suspenseConfig,
);
// Process this update.
// update.eagerReducer : basicStateReducer function
// reducer : basicStateReducer function
//二者相同,走這裏
//獲取 update 的 state 值,新值
if (update.eagerReducer === reducer) {
// If this update was processed eagerly, and its reducer matches the
// current reducer, we can use the eagerly computed state.
newState = ((update.eagerState: any): S); //eagerState: "jin"
} else {
const action = update.action;
newState = reducer(newState, action);
}
}
prevUpdate = update;
update = update.next;
//update.next 就是 last 對象,last 對象就是 first,二者相等,跳出循環
} while (update !== null && update !== first);
if (!didSkip) {
newBaseUpdate = prevUpdate;
newBaseState = newState;
}
// Mark that the fiber performed work, but only if the new state is
// different from the current state.
//判斷 memoizedState(oldState) 和 newState 是否真的不一樣
if (!is(newState, hook.memoizedState)) {
//標記當前 fiber 的確收到了 update
markWorkInProgressReceivedUpdate();
}
hook.memoizedState = newState; //newState = "jin"
hook.baseUpdate = newBaseUpdate;
hook.baseState = newBaseState; //newBaseState = "jin"
queue.lastRenderedState = newState;
}
複製代碼
注意這邊是一個do..while
循環,跳出條件是:
!(update !== null && update !== first)
複製代碼
while
循環中,由於updateExpirationTime
是和renderExpirationTime
相等的,由於:
export function renderWithHooks(
current: Fiber | null,
workInProgress: Fiber,
Component: any, //render
props: any,
refOrContext: any, //ref
nextRenderExpirationTime: ExpirationTime,
):{
//本次 render 時候的優先級
renderExpirationTime = nextRenderExpirationTime;
//...
}
複製代碼
因此不走這邊:
//二者相等,跳過
if (updateExpirationTime < renderExpirationTime) {
}
複製代碼
(6) 而後走這裏:
//走這裏
else {
//刪除了一些代碼
// Process this update.
// update.eagerReducer : basicStateReducer function
// reducer : basicStateReducer function
//二者相同,走這裏
//獲取 update 的 state 值,新值
if (update.eagerReducer === reducer) {
// If this update was processed eagerly, and its reducer matches the
// current reducer, we can use the eagerly computed state.
newState = ((update.eagerState: any): S); //eagerState: "jin"
} else {
const action = update.action;
newState = reducer(newState, action);
}
}
複製代碼
由於update.eagerState
等於2、dispatchAction()
中的queue.lastRenderedReducer
,等於`1、mountState()
中的basicStateReducer
,因此update.eagerReducer === reducer
條件成立,走這邊:
newState = ((update.eagerState: any): S);
複製代碼
此時newState=update.eagerState='jin'
,而後由於update.next
就是last
對象,last
對象就是first
,二者相等,跳出while
循環
(7) 跳出while
循環後走這邊:
hook.memoizedState = newState; //newState = "jin"
hook.baseUpdate = newBaseUpdate;
hook.baseState = newBaseState; //newBaseState = "jin"
}
const dispatch: Dispatch<A> = (queue.dispatch: any);
//[name,setName]
//dispatchAction
return [hook.memoizedState, dispatch];
複製代碼
將'jin'
賦值給hook.memoizedState
,返回['jin', dispatch]
(8) 到開發層面,此時name
已更新爲'jin'
function App() {
debugger
const [name, setName] = React.useState( 'chen');
return (
<div onClick={()=>setName('jin')}>
{name}
</div>
);
}
複製代碼
useState
源碼解析部分就結束了,接下來看下 下面這個問題
若是你時常查閱 React 官方文檔的話,確定會看到這個規則:
zh-hans.reactjs.org/docs/hooks-…
爲何useState
要按順序執行呢?
咱們注意下mountWorkInProgressHook()
的firstWorkInProgressHook
:
由圖能夠看到,當初始化三個 useState 時,Hooks
鏈是經過next
來綁定三個state
的順序的,若是在屢次調用 Hooks 時,將某一個useState
有條件的省略掉,不執行,那麼.next
的時候,獲取的 state 就不是理想的 state,會形成state錯位,因此 React 官方已經禁止這樣作了:
mountState/dispatchAction/renderWithHooks/updateState/updateReducer
:
github.com/AttackXiaoJ…
updateFunctionComponent
:
github.com/AttackXiaoJ…
(完)