Appearance
要区分是
mount
还是update
的过程:在函数beginWork -> renderWithHooks
过程中
js
function renderWithHooks(
current, wip, Component, props, secondArgs, nextRenderLanes
) {
renderLanes = nextRenderLanes;
currentlyRenderingFiber = workInProgress;
// 判断是 创建hook, 还是 更新 hook,赋值到不同的 dispatcher
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
}
hook 的创建和更新
js
// react-conciler/src/ReactFiberHook.js
function mountWorkInProgressHook() {
// 创建hook
const hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null
};
if (workInProgressHook === null) {
// 函数组件第一个 hook,挂载到 当前Fiber 的 memoizedState
currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
} else {
// 之后的 hook,用next指针连接
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}
function updateWorkInProgressHook(): Hook {
let nextCurrentHook: null | Hook;
if (currentHook === null) {
// 从 alternate 获取 currentHook
const current = currentlyRenderingFiber.alternate;
if (current !== null) {
nextCurrentHook = current.memoizedState;
} else {
nextCurrentHook = null;
}
} else {
nextCurrentHook = currentHook.next;
}
let nextWorkInProgressHook: null | Hook;
// 获取 wiphook
if (workInProgressHook === null) {
nextWorkInProgressHook = currentlyRenderingFiber.memoizedState;
} else {
nextWorkInProgressHook = workInProgressHook.next;
}
if (nextWorkInProgressHook !== null) {
workInProgressHook = nextWorkInProgressHook;
nextWorkInProgressHook = workInProgressHook.next;
currentHook = nextCurrentHook;
} else {
// wip上没有定义hook,在currentHook复制过来
currentHook = nextCurrentHook;
const newHook: Hook = {
memoizedState: currentHook.memoizedState,
baseState: currentHook.baseState,
baseQueue: currentHook.baseQueue,
queue: currentHook.queue,
next: null,
};
if (workInProgressHook === null) {
currentlyRenderingFiber.memoizedState =
workInProgressHook = newHook;
} else {
workInProgressHook = workInProgressHook.next = newHook;
}
}
return workInProgressHook;
}
useState
创建
js
function mountState(initialState) {
const hook = mountWorkInProgressHook(); // 创建hook
// 函数初始化
if (typeof initialState === 'function') {
initialState = initialState();
}
// 初始赋值
hook.memoizedState = hook.baseState = initialState
// 创建 hook.queue
const queue = {
pending: null,
interleaved: null,
lanes: NoLanes,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: initialState
};
hook.queue = queue;
// 返回的 更新函数 dispatch 绑定前两个参数
const dispatch =
queue.dispatch =
dispatchSetState.bind(null, currentlyRenderingFiber$1, queue);
return [hook.memoizedState, dispatch];
}
function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
// 判断action返回值
return typeof action === 'function' ? action(state) : action;
}
function dispatchSetState<S, A>(
fiber: Fiber, queue: UpdateQueue<S, A>, action: A
) {
/**
* 获取当前 fiber 更新的优先级,
* 当前 action 要执行的优先级,就是触发当前fiber更新更新的优先级
*/
const lane = requestUpdateLane(fiber);
/**
* 将 action 操作封装成一个 update节点,用于后续构建链表使用
*/
const update: Update<S, A> = {
lane, // 该节点的优先级,即当前fiber的优先级
action, // 操作,可能直接是数值,也可能是函数
hasEagerState: false, // 是否是急切状态
eagerState: null, // 提前计算出结果,便于在render()之前判断是否要触发更新
next: (null: any), // 指向到下一个节点的指针
};
if (isRenderPhaseUpdate(fiber)) {
/**
* 是否是渲染阶段的更新,若是,则拼接到 queue.pending 的后面
*/
enqueueRenderPhaseUpdate(queue, update);
} else {
/**
* 正常执行
* 将 update 形成单向环形链表,并放到 queue.pending 里
* 即 hook.queue.pending 里,存放着 update 的数据
* queue.pending指向到update链表的最后一个元素,pending.next即是第1个元素
*/
enqueueUpdate(fiber, queue, update, lane);
const alternate = fiber.alternate;
if (fiber.lanes === NoLanes
&& (alternate === null || alternate.lanes === NoLanes)) {
/**
* 当前组件不存在更新,那么首次触发状态更新时,就能立刻计算出最新状态,进而与当前状态比较。
* 如果两者一致,则省去了后续render的过程。
* 可以直接执行当前的action,用来提前判断是否需要当前的函数组件fiber节点
* 若新的state与现在的state一样,我们可以直接提前退出,
* 若不相同,则标记该fiber节点是需要更新的;同时计算后的state可以直接用于后面的更新流程,不用再重新计算一次。
* 根据这文档, https://www.51cto.com/article/703718.html
* 比如从0更新到1,此后每次的更新都是1,即使是相同的值,也会再次重新渲染一次,因为两棵树上的fiber节点,
* 在一次更新后,只会有一个fiber节点会消除更新标记,
* 再更新一次,另一个对应的节点才会消除更新标记;再下一次,就会进入到当前的流程,然后直接return
*/
const lastRenderedReducer = queue.lastRenderedReducer;
// 上次render后的reducer,在mount时即 basicStateReducer
if (lastRenderedReducer !== null) {
let prevDispatcher;
// 上次render后的state,mount时为传入的initialState
const currentState = (queue.lastRenderedState: any);
// 计算此次更新的值
const eagerState = lastRenderedReducer(currentState, action);
update.hasEagerState = true; // 表示该节点的数据已计算过了
update.eagerState = eagerState; // 存储计算出来后的数据
if (is(eagerState, currentState)) {
// 若这次得到的state与上次的一样,则不再重新渲染
return;
}
}
}
const eventTime = requestEventTime();
/**
* 将当前的优先级lane和触发时间给到 fiber 和 fiber.alternate,
* 并以 fiber 的父级节点往上到root所有的节点,将 lane 添加他们的 childLanes 属性中,表示该节点的子节点有更新,
* 在 commit 阶段就会更新该 fiber 节点
* 这里面还存在一个任务优先级的调度,暂时先不考虑
*/
const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
if (root !== null) {
entangleTransitionUpdate(root, queue, lane);
}
}
}
更新
js
// 更新时 dispatcher 更换调用 updateState
function updateState(initialState){
// 等于是调用了 updateReduce,预先设置了reducer函数
return updateReducer(basicStateReducer);
}
useReducer
创建
js
function mountReducer<S, I, A>(
reducer: (S, A) => S, // reducer函数
initialArg: I, // 初始
init?: I => S,
): [S, Dispatch<A>] {
// 创建hook结构
const hook = mountWorkInProgressHook();
// 初始化
let initialState;
if (init !== undefined) {
initialState = init(initialArg);
} else {
initialState = ((initialArg: any): S);
}
hook.memoizedState = hook.baseState = initialState;
const queue = (hook.queue = {
pending: null,
dispatch: null,
lastRenderedReducer: reducer,
lastRenderedState: (initialState: any),
});
// 返回更新函数
// 过程类似 dispatchSetState
const dispatch: Dispatch<A> = (queue.dispatch = (dispatchAction.bind(
null,
currentlyRenderingFiber,
queue,
): any));
return [hook.memoizedState, dispatch];
}
更新
js
function updateReducer(reducer, initialArg, init) {
const hook = updateWorkInProgressHook(); // 当前 hook
const queue = hook.queue;
queue.lastRenderedReducer = reducer;
const current = currentHook;
// base 和 pending 两个都是循环链表
let baseQueue = current.baseQueue;
const pendingQueue = queue.pending;
if (pendingQueue !== null) {
if (baseQueue !== null) {
// 合并成一条循环链表
const baseFirst = baseQueue.next;
const pendingFirst = pendingQueue.next;
baseQueue.next = pendingFirst;
pendingQueue.next = baseFirst;
}
// 赋值给 hook 的 base
current.baseQueue = baseQueue = pendingQueue;
queue.pending = null
}
if (baseQueue !== null) {
const first = baseQueue.next;
let newState = current.baseState;
let newBaseState = null;
let newBaseQueueFirst = null;
let newBaseQueueLast = null;
let update = first;
// 根据 baseQueue 更新
do {
// 参照 update对象 根据 优先级 更新
const updateLane = update.lane;
if (!isSubsetOfLanes(renderLanes, updateLane)) {
// 优先级不够跳过
const clone: Update<S, A> = {
lane: updateLane,
action: update.action,
eagerReducer: update.eagerReducer,
eagerState: update.eagerState,
next: (null: any),
};
// 构建 下次使用的环状链表
if (newBaseQueueLast === null) {
newBaseQueueFirst = newBaseQueueLast = clone;
newBaseState = newState;
} else {
newBaseQueueLast = newBaseQueueLast.next = clone;
}
} else {
// 优先级足够
if (newBaseQueueLast !== null) {
// 之前留下的 跳过的链表
const clone: Update<S, A> = {
lane: NoLane,
action: update.action,
eagerReducer: update.eagerReducer,
eagerState: update.eagerState,
next: (null: any),
};
newBaseQueueLast = newBaseQueueLast.next = clone;
}
if (update.eagerReducer === reducer) {
newState = ((update.eagerState: any): S);
} else {
const action = update.action;
// 计算 reducer 函数
newState = reducer(newState, action);
}
}
update = update.next;
} while (update !== null && update !== first);
if (newBaseQueueLast === null) {
newBaseState = newState;
} else {
newBaseQueueLast.next = (newBaseQueueFirst: any);
}
hook.memoizedState = newState;
hook.baseState = newBaseState;
hook.baseQueue = newBaseQueueLast;
queue.lastRenderedState = newState;
}
const dispatch: Dispatch<A> = (queue.dispatch: any);
return [hook.memoizedState, dispatch];
}