React 中useState的执行过程
React在reconcile过程中会根据vnode生成Fiber树,当遇到函数组件时,会执行函数组件获取组件的children,当遇到useState时,会执行useState初始化状态,
使用useState时我们会从react中引入useState,如下:
const {useState} from 'react;
react中的源码如下:
function useState(initialState) {
var dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
function resolveDispatcher() {
var dispatcher = ReactCurrentDispatcher.current;
return dispatcher;
}
执行useState相当于执行ReactCurrentDispatcher.current的useState方法,ReactCurrentDispatcher.current的具体赋值是在react-dom的renderWithHooks函数中,如下:
function renderWithHooks() {
...省略
ReactCurrentDispatcher.current = HooksDispatcherOnMountInDEV;
// 执行函数组件,获取组件子节点
let children = Component(props, secondArg);
// didScheduleRenderPhaseUpdateDuringThisPass 全局flg
if (didScheduleRenderPhaseUpdateDuringThisPass) {
do {
didScheduleRenderPhaseUpdateDuringThisPass = false;
currentHook = null;
workInProgressHook = null;
workInProgress.updateQueue = null;
ReactCurrentDispatcher.current = __DEV__
? HooksDispatcherOnRerenderInDEV
: HooksDispatcherOnRerender;
children = Component(props, secondArg);
} while (didScheduleRenderPhaseUpdateDuringThisPass);
}
}
const HooksDispatcherOnMountInDEV ={
useState: function (initialState) {
//标记当前执行的hook名称
currentHookNameInDev = 'useState';
//处理hookTypesDev,做一些校验
mountHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
try {
return mountState(initialState);
} finally {
ReactCurrentDispatcher$1.current = prevDispatcher;
}
},
}
function mountState(initialState) {
var hook = mountWorkInProgressHook();
//初始化memoizedState 链表对象
//hook= { baseQueue: null
// baseState: null
// memoizedState: null
// next: null
// queue: null
//}
//如果useState初始化传入的是函数则执行这个函数
if (typeof initialState === 'function') {
initialState = initialState();
}
hook.memoizedState = hook.baseState = initialState;
var queue = hook.queue = {
pending: null,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: initialState
};
//将queue保存在Fiberd对象的memoizedState属性上,即保存了state的初始值又保存了setState的函数引用
var dispatch = queue.dispatch = dispatchAction.bind(null, currentlyRenderingFiber$1, queue);
return [hook.memoizedState, dispatch];
}
如上可以拿到初始化的state和setState的函数,当调用setSatate时会指向dispatchAction(fiber, queue, action)函数,其中fiber是当前useState所在的函数组件的fiber对象,queue为fiebr上的上次state(既fiber.memoizedState),action为新的状态,具体调用如下:
React在reconcile过程中会再次执行函数组件获取组件的children,当再次执行useState时,
function renderWithHooks(){
...省略
// 此时的ReactCurrentDispatcher.current 等于HooksDispatcherOnUpdateInDEV,而不是HooksDispatcherOnMountInDEV
ReactCurrentDispatcher.current = HooksDispatcherOnUpdateInDEV;
}
const HooksDispatcherOnUpdateInDEV = {
useState: function (initialState) {
currentHookNameInDev = 'useState';
updateHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
return updateState(initialState);
} finally {
ReactCurrentDispatcher$1.current = prevDispatcher;
}
}
}
function updateState(initialState) {
return updateReducer(basicStateReducer);
}
function updateReducer(reducer, initialArg, init) {
//
var hook = updateWorkInProgressHook();
var queue = hook.queue;
queue.lastRenderedReducer = reducer;
var current = currentHook; // The last rebase update that is NOT part of the base state.
var baseQueue = current.baseQueue; // The last pending update that hasn't been processed yet.
var pendingQueue = queue.pending;
if (pendingQueue !== null) {
// We have new updates that haven't been processed yet.
// We'll add them to the base queue.
if (baseQueue !== null) {
// Merge the pending queue and the base queue.
var baseFirst = baseQueue.next;
var pendingFirst = pendingQueue.next;
baseQueue.next = pendingFirst;
pendingQueue.next = baseFirst;
}
current.baseQueue = baseQueue = pendingQueue;
queue.pending = null;
}
if (baseQueue !== null) {
// We have a queue to process.
var first = baseQueue.next;
var newState = current.baseState;
var newBaseState = null;
var newBaseQueueFirst = null;
var newBaseQueueLast = null;
var update = first;
do {
var updateLane = update.lane;
if (!isSubsetOfLanes(renderLanes, updateLane)) {
// Priority is insufficient. Skip this update. If this is the first
// skipped update, the previous update/state is the new base
// update/state.
var clone = {
lane: updateLane,
action: update.action,
eagerReducer: update.eagerReducer,
eagerState: update.eagerState,
next: null
};
if (newBaseQueueLast === null) {
newBaseQueueFirst = newBaseQueueLast = clone;
newBaseState = newState;
} else {
newBaseQueueLast = newBaseQueueLast.next = clone;
}
currentlyRenderingFiber$1.lanes = mergeLanes(currentlyRenderingFiber$1.lanes, updateLane);
markSkippedUpdateLanes(updateLane);
} else {
// This update does have sufficient priority.
if (newBaseQueueLast !== null) {
var _clone = {
lane: NoLane,
action: update.action,
eagerReducer: update.eagerReducer,
eagerState: update.eagerState,
next: null
};
newBaseQueueLast = newBaseQueueLast.next = _clone;
} // Process this update.
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;
} else {
var action = update.action;
newState = reducer(newState, action);
}
}
update = update.next;
} while (update !== null && update !== first);
if (newBaseQueueLast === null) {
newBaseState = newState;
} else {
newBaseQueueLast.next = newBaseQueueFirst;
} // Mark that the fiber performed work, but only if the new state is
// different from the current state.
if (!objectIs(newState, hook.memoizedState)) {
markWorkInProgressReceivedUpdate();
}
hook.memoizedState = newState;
hook.baseState = newBaseState;
hook.baseQueue = newBaseQueueLast;
queue.lastRenderedState = newState;
}
var dispatch = queue.dispatch;
return [hook.memoizedState, dispatch];
}