文章目录
- mixin的用法:
- useState的简单实现
- 并发模式
- 更新的优先级
- hooks type:
- HooksDispatcherOnMount 和 HooksDispatcherOnUpdate
- beginWork
- mountIndeterminateComponent
- renderWithHooks
- ContextOnlyDispatcher
- mountState
- basicStateReducer
- mountWorkInProgressHook
- dispatchAction
- getCurrentPriorityLevel
- updateFunctionComponent
- updateState
- updateReducer
- updateWorkInProgressHook
- HooksDispatcherOnMount
- HooksDispatcherOnMountInDEV
- rerenderState
- rerenderReducer
- useState
// 排除两种不符合预期的情况
// 1:-0 === +0
// 2: NaN !== NaN
function is(x: any, y: any) {
return (
(x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y)
);
}
性能上:无状态函数组件 > class components > React.createClass()
mixin用的是for in,会复制原型链上的属性,Object.assign不会
mixin的用法:
var ComponentOne = React.createClass({
mixins: [DefaultNameMixin],
})
useState的简单实现
let memoizedState: any[] = [] // hooks 的值存放在这个数组里
let cursor = 0 // 当前 memoizedState 的索引
function useState(initialValue: any) {
memoizedState[cursor] = memoizedState[cursor] || initialValue
const currentCursor = cursor
function setState(newState: any) {
memoizedState[currentCursor] = newState
cursor = 0
render(<App />, document.getElementById('root'))
}
// 返回当前 state,并把 cursor 加 1
return [memoizedState[cursor++], setState]
}
并发模式
1, 同步代码 和 promise 里的 setState 会合并
2, 同时间点的 setTimeout 里的 setState 会合并
更新的优先级
1,useEffect(默认&回调)| promise | setTimeout: 97
2,click(用户交互): 98
3,渲染过程中更新(立即更新):99
hooks type:
export type Hook = {
memoizedState: any, // 指向当前渲染节点 Fiber, 上一次完整更新之后的最终状态值
baseState: any, // 初始化 initialState, 已经每次 dispatch 之后 newState
baseUpdate: Update<any, any> | null, // 当前需要更新的 Update ,每次更新完之后,会赋值上一个 update,方便 react 在渲染错误的边缘,数据回溯
queue: UpdateQueue<any, any> | null, // 缓存的更新队列,存储多次更新行为
next: Hook | null, // link 到下一个 hooks,通过 next 串联每一 hooks
};
type Update<S, A> = {
expirationTime: ExpirationTime, // 当前更新的过期时间
suspenseConfig: null | SuspenseConfig,
action: A,
eagerReducer: ((S, A) => S) | null,
eagerState: S | null,
next: Update<S, A> | null,
priority?: ReactPriorityLevel, // 优先级
};
type UpdateQueue<S, A> = {
last: Update<S, A> | null,
dispatch: (A => mixed) | null,
lastRenderedReducer: ((S, A) => S) | null,
lastRenderedState: S | null,
};
HooksDispatcherOnMount 和 HooksDispatcherOnUpdate
const HooksDispatcherOnMount: Dispatcher = {
readContext,
useCallback: mountCallback,
useContext: readContext,
useEffect: mountEffect,
useImperativeHandle: mountImperativeHandle,
useLayoutEffect: mountLayoutEffect,
useMemo: mountMemo,
useReducer: mountReducer,
useRef: mountRef,
useState: mountState,
useDebugValue: mountDebugValue,
useResponder: createResponderListener,
};
const HooksDispatcherOnUpdate: Dispatcher = {
readContext,
useCallback: updateCallback,
useContext: readContext,
useEffect: updateEffect,
useImperativeHandle: updateImperativeHandle,
useLayoutEffect: updateLayoutEffect,
useMemo: updateMemo,
useReducer: updateReducer,
useRef: updateRef,
useState: updateState,
useDebugValue: updateDebugValue,
useResponder: createResponderListener,
};
beginWork
// function beginWork
case IndeterminateComponent:
{
return mountIndeterminateComponent(current, workInProgress, workInProgress.type, renderExpirationTime);
}
case FunctionComponent: {
// TODO: 这里会将 workInProgress 上的 memoizedState(最新的 hook)拷贝赋值给 current.memoizedState
return updateFunctionComponent(
current,
workInProgress,
Component,
resolvedProps,
renderExpirationTime,
);
}
mountIndeterminateComponent
function mountIndeterminateComponent(_current, workInProgress, Component, renderExpirationTime) {
// if (_current !== null) {
// _current.alternate = null;
// workInProgress.alternate = null;
// workInProgress.effectTag |= Placement;
// }
// var props = workInProgress.pendingProps;
// var context;
// {
// var unmaskedContext = getUnmaskedContext(workInProgress, Component, false);
// context = getMaskedContext(workInProgress, unmaskedContext);
// }
// prepareToReadContext(workInProgress, renderExpirationTime);
// var value;
// {
// if (Component.prototype && typeof Component.prototype.render === 'function') {
// var componentName = getComponentName(Component) || 'Unknown';
// if (!didWarnAboutBadClass[componentName]) {
// error("The <%s /> component appears to have a render method, but doesn't extend React.Component. " + 'This is likely to cause errors. Change %s to extend React.Component instead.', componentName, componentName);
// didWarnAboutBadClass[componentName] = true;
// }
// }
// if (workInProgress.mode & StrictMode) {
// ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null);
// }
// setIsRendering(true);
// ReactCurrentOwner$1.current = workInProgress;
value = renderWithHooks(null, workInProgress, Component, props, context, renderExpirationTime);
// setIsRendering(false);
// }
// workInProgress.effectTag |= PerformedWork;
if (typeof value === 'object' && value !== null && typeof value.render === 'function' && value.$$typeof === undefined) {
// {
// var _componentName = getComponentName(Component) || 'Unknown';
// if (!didWarnAboutModulePatternComponent[_componentName]) {
// error('The <%s /> component appears to be a function component that returns a class instance. ' + 'Change %s to a class that extends React.Component instead. ' + "If you can't use a class try assigning the prototype on the function as a workaround. " + "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + 'cannot be called with `new` by React.', _componentName, _componentName, _componentName);
// didWarnAboutModulePatternComponent[_componentName] = true;
// }
// } // Proceed under the assumption that this is a class instance
workInProgress.tag = ClassComponent; // Throw out any hooks that were used.
// workInProgress.memoizedState = null;
// workInProgress.updateQueue = null; // Push context providers early to prevent context stack mismatches.
// // During mounting we don't know the child context yet as the instance doesn't exist.
// // We will invalidate the child context in finishClassComponent() right after rendering.
// var hasContext = false;
// if (isContextProvider(Component)) {
// hasContext = true;
// pushContextProvider(workInProgress);
// } else {
// hasContext = false;
// }
// workInProgress.memoizedState = value.state !== null && value.state !== undefined ? value.state : null;
// initializeUpdateQueue(workInProgress);
// var getDerivedStateFromProps = Component.getDerivedStateFromProps;
// if (typeof getDerivedStateFromProps === 'function') {
// applyDerivedStateFromProps(workInProgress, Component, getDerivedStateFromProps, props);
// }
// adoptClassInstance(workInProgress, value);
// mountClassInstance(workInProgress, Component, props, renderExpirationTime);
// return finishClassComponent(null, workInProgress, Component, true, hasContext, renderExpirationTime);
} else {
// TODO:标记组件为函数组件
workInProgress.tag = FunctionComponent;
// {
// if ( workInProgress.mode & StrictMode) {
// // Only double-render components with Hooks
// if (workInProgress.memoizedState !== null) {
// value = renderWithHooks(null, workInProgress, Component, props, context, renderExpirationTime);
// }
// }
// }
// reconcileChildren(null, workInProgress, value, renderExpirationTime);
// {
// validateFunctionComponentInDev(workInProgress, Component);
// }
// return workInProgress.child;
}
}
renderWithHooks
// 首次加载或者更新
nextCurrentHook = current !== null ? current.memoizedState : null;
ReactCurrentDispatcher.current =
nextCurrentHook === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
// mountState 和 updateState
// 完整代码
// current 为旧的 Fiber对象,第一次挂载 state 时为null,workInProgress 为新的Fiber对象,两者使用了 双缓冲技术,分别保留对方的引用
// workInProgress.alternate = current;
// current.alternate = workInProgress;
// TODO:在之后的 更新中都是在 workInProgress 上操作,更新完成后会把 current 指向 workInProgress
function renderWithHooks(current, workInProgress, Component, props, secondArg, nextRenderExpirationTime) {
renderExpirationTime = nextRenderExpirationTime;
// TODO: 把 workInProgress 赋值给 当前正在渲染 的 fiber
currentlyRenderingFiber$1 = workInProgress;
/*
{
hookTypesDev = current !== null ? current._debugHookTypes : null;
hookTypesUpdateIndexDev = -1; // Used for hot reloading:
ignorePreviousDependencies = current !== null && current.type !== workInProgress.type;
}
*/
// TODO: 清除 workInProgress.memoizedState
workInProgress.memoizedState = null;
workInProgress.updateQueue = null;
workInProgress.expirationTime = NoWork;
{
if (current !== null && current.memoizedState !== null) {
ReactCurrentDispatcher.current = HooksDispatcherOnUpdateInDEV;
} else if (hookTypesDev !== null) {
ReactCurrentDispatcher.current = HooksDispatcherOnMountWithHookTypesInDEV;
} else {
ReactCurrentDispatcher.current = HooksDispatcherOnMountInDEV;
}
}
var children = Component(props, secondArg);
// TODO: 渲染函数内更新 入口
if (workInProgress.expirationTime === renderExpirationTime) {
var numberOfReRenders = 0;
do {
workInProgress.expirationTime = NoWork;
// TODO: 最大可更新次数 RE_RENDER_LIMIT 为 25
if (!(numberOfReRenders < RE_RENDER_LIMIT)) {
{
throw Error( "Too many re-renders. React limits the number of renders to prevent an infinite loop." );
}
}
numberOfReRenders += 1;
/*
{
ignorePreviousDependencies = false;
}
*/
// TODO: 把这些变量都置空,方便在 updateWorkInProgressHook 重新获取 新旧的hook
currentHook = null;
workInProgressHook = null;
workInProgress.updateQueue = null;
/*
{
// Also validate hook order for cascading updates.
hookTypesUpdateIndexDev = -1;
}
*/
// TODO: 指定新的 useState 为 rerenderState
ReactCurrentDispatcher.current = HooksDispatcherOnRerenderInDEV ;
// TODO: 用新 useState 重新渲染组件
children = Component(props, secondArg);
} while (workInProgress.expirationTime === renderExpirationTime);
}
// TODO: 把 dispatcher 指向 ContextOnlyDispatcher(只能调用reacContext,调用 hook 的其它方法都会报错
ReactCurrentDispatcher.current = ContextOnlyDispatcher;
/*
{
workInProgress._debugHookTypes = hookTypesDev;
} // This check uses currentHook so that it works the same in DEV and prod bundles.
// hookTypesDev could catch more cases (e.g. context) but only in DEV bundles.
*/
// TODO: 判断是否渲染完所有 hook
var didRenderTooFewHooks = currentHook !== null && currentHook.next !== null;
renderExpirationTime = NoWork;
currentlyRenderingFiber$1 = null;
currentHook = null;
workInProgressHook = null;
/*
{
currentHookNameInDev = null;
hookTypesDev = null;
hookTypesUpdateIndexDev = -1;
}
*/
didScheduleRenderPhaseUpdate = false;
if (!!didRenderTooFewHooks) {
{
throw Error( "Rendered fewer hooks than expected. This may be caused by an accidental early return statement." );
}
}
return children;
}
ContextOnlyDispatcher
var ContextOnlyDispatcher = {
readContext: readContext,
useCallback: throwInvalidHookError,
useContext: throwInvalidHookError,
useEffect: throwInvalidHookError,
useImperativeHandle: throwInvalidHookError,
useLayoutEffect: throwInvalidHookError,
useMemo: throwInvalidHookError,
useReducer: throwInvalidHookError,
useRef: throwInvalidHookError,
useState: throwInvalidHookError,
useDebugValue: throwInvalidHookError,
useResponder: throwInvalidHookError,
useDeferredValue: throwInvalidHookError,
useTransition: throwInvalidHookError
};
mountState
// TODO: 第一次调用组件的 useState 时实际调用的方法
function mountState(initialState) {
var hook = mountWorkInProgressHook();
// TODO: 如果是函数 就计算值
if (typeof initialState === 'function') {
initialState = initialState();
}
hook.memoizedState = hook.baseState = initialState;
var queue = hook.queue = {
pending: null,
dispatch: null,
// TODO: 计算其 值 的函数
lastRenderedReducer: basicStateReducer,
lastRenderedState: initialState
};
// TODO: 在之后的更新中: currentlyRenderingFiber$1 用于区分组件,queue 用于区分 hook
var dispatch = queue.dispatch = dispatchAction.bind(null, currentlyRenderingFiber$1, queue);
return [hook.memoizedState, dispatch];
}
basicStateReducer
function basicStateReducer(state, action) {
return typeof action === 'function' ? action(state) : action;
}
mountWorkInProgressHook
// TODO: 创建一个新的 hook
function mountWorkInProgressHook() {
var hook = {
// TODO: 记录当前 useState 应该返回的值
memoizedState: null,
// TODO: 处理更新时,从这里获取初始值
baseState: null,
// TODO: 保存还没有提交的更新(可能是因为 更新的优先级 太低);假如新Fiber上的更新没有提交
baseQueue: null,
// TODO: 更新行为{dispatch, lastRenderReducer, lastRenderState, pending}
queue: null,
// TODO: 下一次 useState 对应的 Hook
next: null,
};
// TODO: workInProgressHook 当前 hook(全局变量)
// TODO: 只有在挂载第一个 state 的时候,workInProgressHook 为空
// TODO: 其他时候 workInProgressHook 为上次执行的 hook
if (workInProgressHook === null) {
// TODO: 会把第一个hook 绑定到当前 fiber对象 的 memoizedState 上,更新时从这里开始读取
currentlyRenderingFiber$1.memoizedState = workInProgressHook = hook;
} else {
// TODO: 上次执行的 hook 的 next 指向 当前hook
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}
dispatchAction
function dispatchAction(fiber, queue, action) {
{
// TODO: 不支持传入回调函数
if (typeof arguments[3] === 'function') {
error("State updates from the useState() and useReducer() Hooks don't support the " + 'second callback argument. To execute a side effect after ' + 'rendering, declare it in the component body with useEffect().');
}
}
var currentTime = requestCurrentTimeForUpdate();
var suspenseConfig = requestCurrentSuspenseConfig();
var expirationTime = computeExpirationForFiber(currentTime, fiber, suspenseConfig);
var update = {
// TODO: 有效时间
expirationTime: expirationTime,
suspenseConfig: suspenseConfig,
action: action,
eagerReducer: null,
eagerState: null,
next: null
};
{
update.priority = getCurrentPriorityLevel();
}
var pending = queue.pending;
if (pending === null) {
// TODO: 创建一个闭环
update.next = update;
} else {
// TODO: 向环中插入新节点
update.next = pending.next;
pending.next = update;
}
queue.pending = update;
var alternate = fiber.alternate;
// TODO: 渲染函数中执行 setState
if (fiber === currentlyRenderingFiber$1 || alternate !== null && alternate === currentlyRenderingFiber$1) {
// TODO: 在其它操作(如 resetHooksAfterThrow)中作为判断标识
didScheduleRenderPhaseUpdate = true;
update.expirationTime = renderExpirationTime;
// TODO: 设置当前 fiber 的更新时间,作为之后 判断 是否有立即更新 操作
currentlyRenderingFiber$1.expirationTime = renderExpirationTime;
} else {
// TODO: 只有第一次更新时第一个 alternate 才为null(两次合并的更新 alternate 都为 null,但 fiber.expirationTime 可能!== NoWork=0,也只有这一次会提前计算新值,其它更新都是在 updateReducer 中计算
// TODO: 如果一次更新中Fiber实际无更新 fiber.expirationTime 会变为 0,且 alternate.expirationTime 也为0,所以下次更新时会再次进入该分支
if (fiber.expirationTime === NoWork && (alternate === null || alternate.expirationTime === NoWork)) {
var lastRenderedReducer = queue.lastRenderedReducer;
if (lastRenderedReducer !== null) {
var prevDispatcher;
{
// TODO: 暂存 ReactCurrentDispatcher.current 计算完新值后 恢复
prevDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
}
try {
var currentState = queue.lastRenderedState;
// TODO: 根据最近一次的 reducer 和最近一次的 state 值计算最新的 state 值,如果在进入render阶段前reducer没有变化那么可以复用eagerState而不用重新再次调用reducer
var eagerState = lastRenderedReducer(currentState, action);
update.eagerReducer = lastRenderedReducer;
update.eagerState = eagerState;
// TODO: 这里检测没有更新会直接返回,如果没有进入这里的无更新操作还是会进入 updateState
if (objectIs(eagerState, currentState)) {
return;
}
} catch (error) {
} finally {
{
// TODO: 恢复 ReactCurrentDispatcher.current
ReactCurrentDispatcher.current = prevDispatcher;
}
}
}
}
/*
{
if ('undefined' !== typeof jest) {
warnIfNotScopedWithMatchingAct(fiber);
warnIfNotCurrentlyActingUpdatesInDev(fiber);
}
}
*/
// TODO: 会更新 fiber 的 expirationTime
scheduleWork(fiber, expirationTime);
}
}
getCurrentPriorityLevel
function getCurrentPriorityLevel() {
switch (Scheduler_getCurrentPriorityLevel()) {
case Scheduler_ImmediatePriority:
return ImmediatePriority;
case Scheduler_UserBlockingPriority:
return UserBlockingPriority$1;
case Scheduler_NormalPriority:
return NormalPriority;
case Scheduler_LowPriority:
return LowPriority;
case Scheduler_IdlePriority:
return IdlePriority;
default:
{
{
throw Error( "Unknown priority level." );
}
}
}
}
updateFunctionComponent
function updateFunctionComponent(current, workInProgress, Component, nextProps, renderExpirationTime) {
/*
{
if (workInProgress.type !== workInProgress.elementType) {
var innerPropTypes = Component.propTypes;
if (innerPropTypes) {
checkPropTypes_1(innerPropTypes, nextProps, // Resolved props
'prop', getComponentName(Component), getCurrentFiberStackInDev);
}
}
}
var context;
{
var unmaskedContext = getUnmaskedContext(workInProgress, Component, true);
context = getMaskedContext(workInProgress, unmaskedContext);
}
var nextChildren;
prepareToReadContext(workInProgress, renderExpirationTime);
*/
{
ReactCurrentOwner$1.current = workInProgress;
setIsRendering(true);
nextChildren = renderWithHooks(current, workInProgress, Component, nextProps, context, renderExpirationTime);
// TODO: blocking模式 或 concurrent 模式 加上严格模式 会进入, 会执行两次 renderWithHooks ,流程基本一致 但只会触发 一次 useEffect
if ( workInProgress.mode & StrictMode) {
// TODO: workInProgress.memoizedState 时 更新后 的 最新 hook
if (workInProgress.memoizedState !== null) {
nextChildren = renderWithHooks(current, workInProgress, Component, nextProps, context, renderExpirationTime);
}
}
setIsRendering(false);
}
// TODO: 没有涉及更新
if (current !== null && !didReceiveUpdate) {
// TODO: 将 current.expirationTime 设置为 0
bailoutHooks(current, workInProgress, renderExpirationTime);
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderExpirationTime);
}
/*
workInProgress.effectTag |= PerformedWork;
reconcileChildren(current, workInProgress, nextChildren, renderExpirationTime);
*/
// TODO: 继续更新子节点
return workInProgress.child;
}
updateState
function updateState(initialState) {
// TODO: state 都是用的 相同的 reducer 函数
return updateReducer(basicStateReducer, initialState);
}
updateReducer
function updateReducer(reducer, initialArg, init) {
// TODO: 获取当前正在工作中的 hook,并根据此创建一个新的hook(next 指向 null,其他的不变)
var hook = updateWorkInProgressHook();
var queue = hook.queue;
if (!(queue !== null)) {
{
throw Error( "Should have a queue. This is likely a bug in React. Please file an issue." );
}
}
// TODO: useState 情况下,两者是相等的
queue.lastRenderedReducer = reducer;
// TODO: 这里获取的时 旧Fiber 上的 hook
var current = currentHook;
// TODO: 还没有提交的更新(低优先级等)
var baseQueue = current.baseQueue;
var pendingQueue = queue.pending;
if (pendingQueue !== null) {
if (baseQueue !== null) {
// TODO: 合并两个环
var baseFirst = baseQueue.next;
var pendingFirst = pendingQueue.next;
baseQueue.next = pendingFirst;
pendingQueue.next = baseFirst;
}
// TODO: 会将更新队列作为记录保存在 旧Fiber 上
current.baseQueue = baseQueue = pendingQueue;
queue.pending = null;
}
if (baseQueue !== null) {
// TODO: 从环的第二个节点开始,第一个节点为最后一次更新
var first = baseQueue.next;
var newState = current.baseState;
var newBaseState = null;
var newBaseQueueFirst = null;
var newBaseQueueLast = null;
var update = first;
do {
var updateExpirationTime = update.expirationTime;
// TODO: 超时了的更新
/*
if (updateExpirationTime < renderExpirationTime) {
var clone = {
expirationTime: update.expirationTime,
suspenseConfig: update.suspenseConfig,
action: update.action,
eagerReducer: update.eagerReducer,
eagerState: update.eagerState,
next: null
};
if (newBaseQueueLast === null) {
newBaseQueueFirst = newBaseQueueLast = clone;
newBaseState = newState;
} else {
newBaseQueueLast = newBaseQueueLast.next = clone;
}
if (updateExpirationTime > currentlyRenderingFiber$1.expirationTime) {
currentlyRenderingFiber$1.expirationTime = updateExpirationTime;
markUnprocessedUpdateTime(updateExpirationTime);
}
*/
} else {
/*
// This update does have sufficient priority.
if (newBaseQueueLast !== null) {
var _clone = {
expirationTime: Sync,
suspenseConfig: update.suspenseConfig,
action: update.action,
eagerReducer: update.eagerReducer,
eagerState: update.eagerState,
next: null
};
newBaseQueueLast = newBaseQueueLast.next = _clone;
} // 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.
*/
// TODO: 判断计算函数是否有改变,有的话 重新计算值
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;
// TODO: update !== first 来判断队列是否结束
} while (update !== null && update !== first);
if (newBaseQueueLast === null) {
newBaseState = newState;
} else {
newBaseQueueLast.next = newBaseQueueFirst;
}
// TODO: 判断是否相等来决定更新
if (!objectIs(newState, hook.memoizedState)) {
// TODO: didReceiveUpdate = true
markWorkInProgressReceivedUpdate();
}
// TODO: 更新 新Fiber 上的 hook
hook.memoizedState = newState;
hook.baseState = newBaseState;
hook.baseQueue = newBaseQueueLast;
queue.lastRenderedState = newState;
}
// TODO: 这里没有重新bind,所以后面的 dispatch 绑定的还是最初始的 Fiber
var dispatch = queue.dispatch;
return [hook.memoizedState, dispatch];
}
updateWorkInProgressHook
function updateWorkInProgressHook() {
var nextCurrentHook;
// TODO: (全局变量)保存本次渲染中当前的hook,第一个useState时该值为null,从 currentlyRenderingFiber$1.alternate 上获取,其余情况为上一次的hook
if (currentHook === null) {
// TODO: 从 新Fiber 的 alternate(指向 旧Fiber) 上拿到 hook 队列
var current = currentlyRenderingFiber$1.alternate;
if (current !== null) {
nextCurrentHook = current.memoizedState;
} else {
nextCurrentHook = null;
}
} else {
// TODO: 从第二个 useState 起开始根据 currentHook 拿当前hook
nextCurrentHook = currentHook.next;
}
var nextWorkInProgressHook;
if (workInProgressHook === null) {
// TODO: workInProgressHook 在第一个useState时为null,从currentlyRenderingFiber$1.memoizedState (即 currentlyRenderingFiber$1 或 workInProgress 对应的 旧Fiber对象中取(之前 workInProgress.memoizedState 被清除了,所以拿到的也是null)
nextWorkInProgressHook = currentlyRenderingFiber$1.memoizedState;
} else {
// TODO: workInProgressHook 从第二个 useState 起为最近渲染的 hook 的复制体,next 为 null
nextWorkInProgressHook = workInProgressHook.next;
}
if (nextWorkInProgressHook !== null) {
// TODO: 渲染函数中的 state更新 会在这里处理
workInProgressHook = nextWorkInProgressHook;
nextWorkInProgressHook = workInProgressHook.next;
currentHook = nextCurrentHook;
} else {
// Clone from the current hook.
// TODO: 如果 nextCurrentHook 为 null,说明 旧的hook 已经更新完了
if (!(nextCurrentHook !== null)) {
{
throw Error( "Rendered more hooks than during the previous render." );
}
}
currentHook = nextCurrentHook;
// TODO: 创建一个新的 hook
var newHook = {
memoizedState: currentHook.memoizedState,
baseState: currentHook.baseState,
baseQueue: currentHook.baseQueue,
queue: currentHook.queue,
next: null
};
if (workInProgressHook === null) {
// TODO: 第一个 useState 时 workInProgressHook 为 null ,把最新的 hook 复制体 赋值给 currentlyRenderingFiber$1.memoizedState, 更新 当前渲染中的 Fiber 的 memoizedState
currentlyRenderingFiber$1.memoizedState = workInProgressHook = newHook;
} else {
// TODO: 后面的更新都 append 到 新Fiber 的 memoizedState
workInProgressHook = workInProgressHook.next = newHook;
}
}
return workInProgressHook;
}
HooksDispatcherOnMount
const HooksDispatcherOnMount: Dispatcher = {
readContext,
useCallback: mountCallback,
useContext: readContext,
useEffect: mountEffect,
useImperativeHandle: mountImperativeHandle,
useLayoutEffect: mountLayoutEffect,
useMemo: mountMemo,
useReducer: mountReducer,
useRef: mountRef,
useState: mountState,
useDebugValue: mountDebugValue,
useResponder: createResponderListener,
};
HooksDispatcherOnMountInDEV
useState(initialState) {
/*
currentHookNameInDev = 'useState';
mountHookTypesDev();
const prevDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
try {
*/
return mountState(initialState);
/*
} finally {
ReactCurrentDispatcher.current = prevDispatcher;
}
*/
},
rerenderState
function rerenderState(initialState) {
return rerenderReducer(basicStateReducer);
}
rerenderReducer
// TODO: 相较于 updateReducer 更简单,不会去判断 优先级 和 过期时间
function rerenderReducer(reducer, initialArg, init) {
var hook = updateWorkInProgressHook();
var queue = hook.queue;
if (!(queue !== null)) {
{
throw Error( "Should have a queue. This is likely a bug in React. Please file an issue." );
}
}
queue.lastRenderedReducer = reducer; // This is a re-render. Apply the new render phase updates to the previous
// work-in-progress hook.
var dispatch = queue.dispatch;
var lastRenderPhaseUpdate = queue.pending;
var newState = hook.memoizedState;
if (lastRenderPhaseUpdate !== null) {
// The queue doesn't persist past this render pass.
queue.pending = null;
var firstRenderPhaseUpdate = lastRenderPhaseUpdate.next;
var 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.
var action = update.action;
newState = reducer(newState, action);
update = update.next;
} while (update !== firstRenderPhaseUpdate); // 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; // Don't persist the state accumulated 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.baseQueue === null) {
hook.baseState = newState;
}
queue.lastRenderedState = newState;
}
return [newState, dispatch];
}
useState
function useState(initialState) {
var dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}