Open yanlele opened 1 year ago
流程图如下:renderWithHooks 根据current来判断当前是首次渲染还是更新。 hooks加载时调用对应的mount函数,更新时调用对应的update函数。 hooks生成单向链表,通过next连接,最后一个next指向null。 state hooks会生成update循环链表, effects会生成另外一个effectList循环链表。
react-reconciler/src/ReactFiberHooks.js
// renderWithHooks中判断是否是首次渲染 function renderWithHooks(current, workInProgress, Component, props, nextRenderLanes) { //当前正在渲染的车道 renderLanes = nextRenderLanes currentlyRenderingFiber = workInProgress; //函数组件更新队列里存的effect workInProgress.updateQueue = null; //函数组件状态存的hooks的链表 workInProgress.memoizedState = null; //如果有老的fiber,并且有老的hook链表 if (current !== null && current.memoizedState !== null) { ReactCurrentDispatcher.current = HooksDispatcherOnUpdate; } else { ReactCurrentDispatcher.current = HooksDispatcherOnMount; } //需要要函数组件执行前给ReactCurrentDispatcher.current赋值 const children = Component(props); currentlyRenderingFiber = null; workInProgressHook = null; currentHook = null; renderLanes = NoLanes; return children; }
HooksDispatcherOnMount和HooksDispatcherOnUpdate对象分别存放hooks的挂载函数和更新函数
HooksDispatcherOnMount和HooksDispatcherOnUpdate
function resolveDispatcher() { return ReactCurrentDispatcher.current; } /** * * @param {*} reducer 处理函数,用于根据老状态和动作计算新状态 * @param {*} initialArg 初始状态 */ export function useState(initialState) { const dispatcher = resolveDispatcher(); return dispatcher.useState(initialState); }
/** * 构建新的hooks, 其主要作用是在 Fiber 树中遍历到某个组件时, * 根据该组件的类型和当前处理阶段(mount 或 update),处理该组件的 Hook 状态。 */ function updateWorkInProgressHook() { //获取将要构建的新的hook的老hook if (currentHook === null) { const current = currentlyRenderingFiber.alternate; currentHook = current.memoizedState; } else { currentHook = currentHook.next; } //根据老hook创建新hook const newHook = { memoizedState: currentHook.memoizedState, queue: currentHook.queue, next: null, baseState: currentHook.baseState, baseQueue: currentHook.baseQueue } if (workInProgressHook === null) { currentlyRenderingFiber.memoizedState = workInProgressHook = newHook; } else { workInProgressHook = workInProgressHook.next = newHook; } return workInProgressHook; }
接收一个初始状态值,返回一个数组,包含当前状态值和更新状态值的方法。可以通过调用更新方法来改变状态值,并触发组件的重新渲染
//useState其实就是一个内置了reducer的useReducer /** * hook的属性 * hook.memoizedState 当前 hook真正显示出来的状态 * hook.baseState 第一个跳过的更新之前的老状态 * hook.queue.lastRenderedState 上一个计算的状态 */ function mountState(initialState) { const hook = mountWorkInProgressHook(); hook.memoizedState = hook.baseState = initialState; const queue = { pending: null, dispatch: null, lastRenderedReducer: baseStateReducer,//上一个reducer lastRenderedState: initialState//上一个state } hook.queue = queue; const dispatch = (queue.dispatch = dispatchSetState.bind(null, currentlyRenderingFiber, queue)); return [hook.memoizedState, dispatch]; } function dispatchSetState(fiber, queue, action) { // 获取当前的更新赛道 1 const lane = requestUpdateLane(); const update = { lane,//本次更新优先级就是1 action, hasEagerState: false,//是否有急切的更新 eagerState: null,//急切的更新状态 next: null } const alternate = fiber.alternate; //当你派发动作后,我立刻用上一次的状态和上一次的reducer计算新状态 //只要第一个更新都能进行此项优化 if (fiber.lanes === NoLanes && (alternate === null || alternate.lanes == NoLanes)) { //先获取队列上的老的状态和老的reducer const { lastRenderedReducer, lastRenderedState } = queue; //使用上次的状态和上次的reducer结合本次action进行计算新状态 const eagerState = lastRenderedReducer(lastRenderedState, action); update.hasEagerState = true; update.eagerState = eagerState; if (Object.is(eagerState, lastRenderedState)) { return; } } //下面是真正的入队更新,并调度更新逻辑 const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane); const eventTime = requestEventTime(); scheduleUpdateOnFiber(root, fiber, lane, eventTime); } //useState其实就是一个内置了reducer的useReducer function baseStateReducer(state, action) { return typeof action === 'function' ? action(state) : action; } function updateState(initialState) { return updateReducer(baseStateReducer, initialState); } function updateReducer(reducer) { const hook = updateWorkInProgressHook(); const queue = hook.queue; queue.lastRenderedReducer = reducer; const current = currentHook; 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; } 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; do { const updateLane = update.lane; const shouldSkipUpdate = !isSubsetOfLanes(renderLanes, updateLane); if (shouldSkipUpdate) { const clone = { lane: updateLane, action: update.action, hasEagerState: update.hasEagerState, eagerState: update.eagerState, next: null, }; // 省略部分代码 hook.memoizedState = newState; hook.baseState = newBaseState; hook.baseQueue = newBaseQueueLast; queue.lastRenderedState = newState; } if (baseQueue === null) { queue.lanes = NoLanes; } const dispatch = queue.dispatch; return [hook.memoizedState, dispatch]; }
参考文档:
hooks 的实现原理
流程图如下:renderWithHooks 根据current来判断当前是首次渲染还是更新。 hooks加载时调用对应的mount函数,更新时调用对应的update函数。 hooks生成单向链表,通过next连接,最后一个next指向null。 state hooks会生成update循环链表, effects会生成另外一个effectList循环链表。
renderWithHooks
react-reconciler/src/ReactFiberHooks.js
HooksDispatcherOnMount和HooksDispatcherOnUpdate
对象分别存放hooks的挂载函数和更新函数hooks的注册
useState 实现
接收一个初始状态值,返回一个数组,包含当前状态值和更新状态值的方法。可以通过调用更新方法来改变状态值,并触发组件的重新渲染
参考文档