React 触发更新的方式共有 5 种:
ReactDOM.render
this.setState
this.forceUpdate
useState
useReducer
其中,useState (mountState) 是 reducer 参数为 basicStateReducer (basicStateReducer)的 useReducer(mountReducer) 。
所以在更新时,useState 和 useReducer 实质调用的是同一个方法 updateReducer
HooksDispatcherOnUpdateInDEV = {
useReducer: function (reducer, initialArg, init) {
return updateReducer(reducer, initialArg, init);
},
useState: function (initialState) {
return updateState(initialState);
},
}
function updateState(initialState) {
return updateReducer(basicStateReducer);
}
所以,在学习 useState 的更新流程之后,也就知道 useReducer 的更新流程了。
让我们开始。
const App = () => {
const [a, setA] = useState(1);
return (
<>
<div onClick={() => {
setA(1);
setA(2);
setA(3);
}}>{a}</div>
{/*忽略其他无用代码*/}
</>
)
}
当点击后,会 setData 三次,整个过程大概是这样的:
setA --> dispatch (dispatchAction) --> scheduleUpdateOnFiber --> ensureRootIsScheduled --> ...Scheduled... --> performSyncWorkOnRoot --> ... -> beginWork --> updateFunctionComponent --> updateFunctionComponent --> renderWithHooks --> Component --> updateReducer --> ...
其中每次 setA 都会经过的方法是:
setA --> dispatch (dispatchAction) --> scheduleUpdateOnFiber --> ensureRootIsScheduled --> ...Scheduled...
而经过调度后,开始 render 阶段时,三次 setA 已经执行完毕了,数据保存在 hook.queue 里,可以看一下 dispatchAction :
function dispatchAction<S, A>(
fiber: Fiber,
queue: UpdateQueue<S, A>,
action: A,
) {
// 忽略无关代码
const pending = queue.pending;
if (pending === null) {
// This is the first update. Create a circular list.
update.next = update;
} else {
update.next = pending.next;
pending.next = update;
}
queue.pending = update;
}
注意看下 mountReducer 或 mountState 的 queue 的赋值和 dispatchAction 的参数:
const queue = (hook.queue = {
// ...
});
const dispatch: Dispatch<
BasicStateAction<S>,
> = (queue.dispatch = (dispatchAction.bind(
null,
currentlyRenderingFiber,
queue,
): any));
即可知,update 是保存在 hook.queue 上的。
当调度后,开始 render 时,会经过 renderWithHooks 这个方法,这个方法内会执行 Component 方法,也就是组件本身的 function 方法,组件内部的 useState 会执行,根据条件判断:
if (current !== null && current.memoizedState !== null) {
ReactCurrentDispatcher$1.current = HooksDispatcherOnUpdateInDEV;
}
进而调用到 updateReducer 方法去更新 hook 的值。接下来就到 commit 阶段的 commitMutation 里更新对应的 fiber,最终切换 current 树和 workInProgress 树,将更新呈现到屏幕上来了。
参考:
[dispatchAction](