前言
从本篇开始,我们讲 React-Hooks 最常用的几个函数,先通过例子来看下React.useState()
:
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()
一、mountState()
作用:
初始化useState()
,并返回一个数组
源码:
代码语言:javascript复制//第一次更新 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 链表
mountWorkInProgressHook()
源码如下:
//将当前 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
的话,比如:
const [styleObj, setStyleObj] = React.useState(()=>{return {height:14,} });
那么就会去执行initialState
:
//如果 initValue 是 function 的话,则获取执行的结果
if (typeof initialState === 'function') {
initialState = initialState();
}
(3) 然后初始化了hook.memoizedState
、hook.baseState
、hook.queue
和queue.dispatch
(4) basicStateReducer
的源码如下:
//传入参数 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()
的使用:
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) 最终返回一个数组:
代码语言:javascript复制[hook.memoizedState, dispatch]
在开发层面
chen
赋值给了hook.memoizedState
setName
表示dispatch
,注意,这里 setName 只是形参,并没有赋值给 dispatch
(7) 此时的name 为'chen',当点击 div,调用setName('jin')
时,会执行dispatchAction('jin')
函数,我们来看下dispatchAction
源码
二、dispatchAction()
作用:
(1) 新建 update 对象:{action:'jin'}
(2) 将 update 加至 hook.queue 的末尾:hook.queue.last = update
(3) 执行 scheduleWork(),走 updateFunctionComponent()
流程
源码:
代码语言:javascript复制//1、新建 update 对象:{action:'jin'}
//2、将 update 加至 hook.queue 的末尾:hook.queue.last = update
//3、执行 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) 所以,以下条件不成立:
代码语言:javascript复制 //由于这边的currentlyRenderingFiber为 null,传进来的fiber(currentlyRenderingFiber)有值,所以走 else 情况
if (
fiber === currentlyRenderingFiber ||
(alternate !== null && alternate === currentlyRenderingFiber)
) {
}
看下 else 情况
(3) 新建 update 对象:
代码语言:javascript复制 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
在一、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(下)
三、从updateFunctionComponent()到updateState()
(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')
!!
四、updateState()
作用:
多次更新initialState
的值
源码:
代码语言:javascript复制//多次更新 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()
的作用及使用,请看:
https://zh-hans.reactjs.org/docs/hooks-reference.html#usereducer
(2) 注意此时的initialState
仍然是'chen'
五、updateReducer()
作用:
多次更新initialState
的值
源码:
代码语言:javascript复制//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;
//...
}
所以不走这边:
代码语言:javascript复制 //两者相等,跳过
if (updateExpirationTime < renderExpirationTime) {
}
(6) 然后走这里:
代码语言:javascript复制//走这里
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
等于二、dispatchAction()
中的queue.lastRenderedReducer
,等于一、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
源码解析部分就结束了,接下来看下 下面这个问题
六、为什么useState要按顺序执行
如果你时常查阅 React 官方文档的话,肯定会看到这个规则:
https://zh-hans.reactjs.org/docs/hooks-rules.html#explanation
为什么useState
要按顺序执行呢?
我们注意下mountWorkInProgressHook()
的firstWorkInProgressHook
:
由图可以看到,当初始化三个 useState 时,Hooks
链是通过next
来绑定三个state
的顺序的,如果在多次调用 Hooks 时,将某一个useState
有条件的省略掉,不执行,那么.next
的时候,获取的 state 就不是理想的 state,会造成state错位,所以 React 官方已经禁止这样做了:
七、useState流程图
八、GitHub
mountState/dispatchAction/renderWithHooks/updateState/updateReducer
:
https://github.com/AttackXiaoJinJin/reactExplain/blob/master/react16.8.6/packages/react-reconciler/src/ReactFiberHooks.js
updateFunctionComponent
:
https://github.com/AttackXiaoJinJin/reactExplain/blob/master/react16.8.6/packages/react-reconciler/src/ReactFiberBeginWork.js