如果发现有问题或理解不到位等,请及时联系作者、评论指出
const initValue = 0 //默认值
const [count,setCount] = useState(initValue) // 声明
setCount(1) // 更新下次渲染的count值为 无返回值
function useState<S>(initialState: (() => S) | S,)
:[S, Dispatch<BasicStateAction<S>>]
{
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
function resolveDispatcher() {
const dispatcher = ReactCurrentDispatcher.current;
return dispatcher;
}
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount // 挂载
: HooksDispatcherOnUpdate; // 更新
function mountState(initialState){
// TO SKIP
const hook = mountWorkInProgressHook();
//判断mountState传入的initialState是函数还是值,是函数就运行将返回值赋值给initialState
if(typeof initialState === 'function'){
initialState = initialState();
}
//接下来,将initialState赋值给hook的baseState和hook的memoizedState
hook.memoizedState = hook.baseState = initialState;
const queue = {
pending: null, // 等待
lanes: NoLanes, // 更新优先级
dispatch: null, // setState方法
lastRenderedReducer: basicStateReducer, // TO SKIP
lastRenderedState: (initialState: any),
};
//将创建的queue赋值给hook的queue
hook.queue = queue;
// TO SKIP
const dispatch =
(queue.dispatch =
(dispatchSetState.bind(null,currentlyRenderingFiber,queue,)));
return [hook.memoizedState, dispatch];
}
//首先调用mountWorkInProgressHook,创建一个hook存储状态,如果当前工作中没有钩子,
//就将创建的hook赋值给当前工作中钩子,如果当前工作中有钩子,就将当前hook赋值给当前工
//作中钩子的next(相当于链表的next),最后返回当前工作中的hook,并用hook变量储存后续使用。
function mountWorkInProgressHook() {
const hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null,
};
if (workInProgressHook === null) {
currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
} else {
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}
//如果是方法则调用否则返回传入的
function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
return typeof action === 'function' ? action(state) : action;
}
function dispatchSetState<S, A>(fiber: Fiber,queue: UpdateQueue<S, A>,action: A,){
const lane = requestUpdateLane(fiber); // 更新优先级
const update: Update<S, A> = {
lane, // 更新优先级
action, // 更新动作
hasEagerState: false, // 是否紧急更新
eagerState: null, // 紧急更新对象
next: (null: any),
};
//判断传入的fiber是否在渲染阶段更新
if (isRenderPhaseUpdate(fiber)) {
// TO SKIP
enqueueRenderPhaseUpdate(queue, update);
} else{
//获取当前fiber节点的alternate节点(当前fiber节点的上一个节点)
const alternate = fiber.alternate;
//判断当前fiber节点的lanes属性是否为NoLanes并且判断alternate节点是否为空或者
//其lanes属性是否为NoLanes,其作用是用来确定当前任务是否需要被渲染
if(fiber.lanes === NoLanes &&
(alternate === null || alternate.lanes === NoLanes)){
//判断lastRenderedReducer(队列)是否为空
const lastRenderedReducer = queue.lastRenderedReducer;
if (lastRenderedReducer !== null) {
try {
//从queue对象的lastRenderedState属性获取当前状态,
//并将其赋值给名为currentState的常量
const currentState = queue.lastRenderedState;
//使用currentState和传入的action,
//通过调用lastRenderedReducer函数来计算新的状态
const eagerState = lastRenderedReducer(currentState, action);
//将eagerState(急切状态)以及还原器已经执行过的标志(hasEagerState)
//存储在update
update.hasEagerState = true;
update.eagerState = eagerState;
//判断急切状态是否与当前状态相同。
if (is(eagerState, currentState)) {
//调用enqueueConcurrentHookUpdateAndEagerlyBailout函数
//来调度一个优化后的更新操作
//并直接返回,不再触发React组件的重新渲染
enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update);
return;
}
}
}
}
//TO SKIP
const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
if (root !== null) {
//进行一系列调度更新
const eventTime = requestEventTime(); // 异步、合并更新原理
scheduleUpdateOnFiber(root, fiber, lane, eventTime); // 调度更新
entangleTransitionUpdate(root, queue, lane); // 调度更新
}
}
markUpdateInDevTools(fiber, lane, action); // 调度更新
}
function enqueueRenderPhaseUpdate(queue,update) {
didScheduleRenderPhaseUpdateDuringThisPass //当前遍历fiber树的过程中是否
//发生了 render phase update
= didScheduleRenderPhaseUpdate //当前渲染过程中,是否存在多个组件尝试触发状态更新
= true;
const pending = queue.pending;
if (pending === null) {
// 第一次更新,并且创建一个循环的列表
update.next = update;
} else {
//向循环列表插入update
update.next = pending.next;
pending.next = update;
}
//将update赋值给queue的pending
queue.pending = update;
}
function enqueueConcurrentHookUpdate(fiber,queue,update,lane):FiberRoot{
// TO SKIP
enqueueUpdate(fiber, concurrentQueue, concurrentUpdate, lane);
return getRootForUpdatedFiber(fiber);
}
//将更新任务加入到并发队列中,同时更新相关的lanes信息
function enqueueUpdate(fiber,queue,update,lane,) {
concurrentQueues[concurrentQueuesIndex++] = fiber;
concurrentQueues[concurrentQueuesIndex++] = queue;
concurrentQueues[concurrentQueuesIndex++] = update;
concurrentQueues[concurrentQueuesIndex++] = lane;
concurrentlyUpdatedLanes = mergeLanes(concurrentlyUpdatedLanes, lane);
fiber.lanes = mergeLanes(fiber.lanes, lane);
const alternate = fiber.alternate;
if (alternate !== null) {
alternate.lanes = mergeLanes(alternate.lanes, lane);
}
}
function getRootForUpdatedFiber(sourceFiber): FiberRoot | null {
let node = sourceFiber;//将传入的fiber储存为node
let parent = node.return;//将node的return属性(父fiber)存储为parent
//如果parent不为空则一直去找他的父fiber,直到为null为止(找到fiber根结点)
while (parent !== null) {
node = parent;
parent = node.return;
}
//如果node.tag等于HostRoot(根结点),则返回node.stateNode。
//否则(即未找到根节点或不是HostRoot类型),返回null
return node.tag === HostRoot ? (node.stateNode: FiberRoot) : null;
}
个人理解(已同步到源码中,可忽略)
useStat是通过调用resolveDispatcher返回dispatcher的userState,通过当前状态判断是否更新或者挂载。挂载:HooksDispatcherOnMount调用mountState返回[hook.memoizedState, dispatch]
首先调用mountWorkInProgressHook,创建一个hook存储状态,如果当前工作中没有钩子,就将创建的hook赋值给当前工作中钩子,如果当前工作中有钩子,就将当前hook赋值给当前工作中钩子的next(相当于链表的next),最后返回当前工作中的hook,并用hook变量储存后续使用。
其次,判断mountState传入的initialState是函数还是值,如果是函数就运行将返回值赋值给initialState。
接下来,将initialState赋值给hook的baseState和hook的memoizedState。
接下来,创建一个队列queue,属性有pending(等待),lanes(优先级),dispatch(setState方法),
lastRenderedReducer(basicStateReducer,就是reducer的一种类型),lastRenderedState(initialState),将创建的queue赋值给hook的queue。
接下来,调用dispatchSetState方法,更新优先级,创建update,属性有lane(优先级),action(动作),hasEagerState(是否紧急更新),eagerState(紧急更新对象),next(下一个)。判断传入的fiber是否在渲染阶段更新。
如果在,调用enqueueRenderPhaseUpdate,将didScheduleRenderPhaseUpdate(当前批次的渲染过程中,是否存在一个或多个组件尝试触发状态更新)和didScheduleRenderPhaseUpdateDuringThisPass(当前遍历fiber树的过程中是否发生了render phase update),接着判断queue的pending是否为null(则为第一次更新),如果为null,是第一次更新,并且创建一个循环的列表。如果不为null,则将pending的next赋值给update的next,将update赋值给pending的next,构成一个循环列表。最后将update赋值给queue的pending。
如果不在,获取当前fiber节点的alternate节点,alternate节点是当前fiber节点的上一个节点,接着判断当前fiber节点的lanes属性是否为NoLanes并且判断alternate节点是否为空或者其lanes属性是否为NoLanes,其作用是用来确定当前任务是否需要被渲染。如果需要被渲染,判断lastRenderedReducer(队列)是否为空,如果不为空,则将从queue对象的lastRenderedState属性获取当前状态,并将其赋值给名为currentState的常量,使用上一步获取到的当前状态和传入的动作(action),通过调用lastRenderedReducer函数来计算新的状态,将“急切状态”(eagerState)以及还原器已经执行过的标志(hasEagerState)存储在update对象中。这样,在进入渲染阶段时,如果还原器没有改变,则可以直接使用这个急切状态而无需再次调用还原器。接下来,判断急切状态是否与当前状态相同。如果两者相等,意味着当前动作并未引起状态的实际变化,函数会选择快速路径,通过调用enqueueConcurrentHookUpdateAndEagerlyBailout函数来调度一个优化后的更新操作,并直接返回,不再触发React组件的重新渲染。接着调用enqueueConcurrentHookUpdate方法,里面调用enqueueUpdate将更新任务加入到并发队列中,同时更新相关的lanes信息。返回getRootForUpdatedFiber,这个方法将传入的fiber储存为node,同时将node的return属性(父fiber)
存储为parent,如果parent不为空则一直去找他的父fiber,直到为null为止(找到fiber根结点),如果node.tag等于HostRoot(根结点),则返回node.stateNode。否则(即未找到根节点或不是HostRoot类型),返回null。
接下来,如果返回的根节点不为nul,则进行一系列调度更新
实现小Demo(与源码会有偏差,只做理解参考)
function myUseState() {
let isMount = true;
let workInProgressHook;
const fiber = {
stateNode: App,
memorizedState: null,
};
function useState(initialState) {
let hook;
if (isMount) {
hook = {
memorizedState: initialState,
next: null,
queue: {
pending: null,
},
};
if (!fiber.memorizedState) {
fiber.memorizedState = hook;
} else {
workInProgressHook.next = hook;
}
workInProgressHook = hook;
} else {
hook = workInProgressHook;
workInProgressHook = workInProgressHook.next;
}
let baseState = hook.memorizedState;
if (hook.queue.pending) {
let firstUpdate = hook.queue.pending.next;
do {
const action = firstUpdate.action;
if (typeof action === "function") {
baseState = action(baseState);
} else {
baseState = action;
}
firstUpdate = firstUpdate.next;
} while (firstUpdate !== hook.queue.pending);
hook.queue.pending = null;
}
hook.memorizedState = baseState;
return [baseState, dispatchAction.bind(null, hook.queue)];
}
function schedule() {
workInProgressHook = fiber.memorizedState;
const app = fiber.stateNode();
isMount = false;
return app;
}
function dispatchAction(queue, action) {
const updateState = {
action,
next: null,
};
if (queue.pending === null) {
updateState.next = updateState;
} else {
updateState.next = queue.pending.next;
queue.pending.next = updateState;
}
queue.pending = updateState;
schedule();
}
function App() {
const [num, updateNum] = myUseState(0);
const [num1, updateNum1] = myUseState(0);
console.log(`${isMount ? "mount" : "update"} num: `, num);
console.log("num1:", num1);
return {
click() {
updateNum((num) => num + 10);
updateNum1(20);
},
};
}
window.app = schedule();
}