Skip to content
On this page

要区分是 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];
}