React useState 是如何实现的?

发布于 2023-07-14 13:49:52 字数 6320 浏览 38 评论 0

hooks 的实现原理

流程图如下:renderWithHooks 根据 current 来判断当前是首次渲染还是更新。 hooks 加载时调用对应的 mount 函数,更新时调用对应的 update 函数。
hooks 生成单向链表,通过 next 连接,最后一个 next 指向 null。 state hooks 会生成 update 循环链表, effects 会生成另外一个 effectList 循环链表。

renderWithHooks

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 的挂载函数和更新函数

hooks 的注册

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 实现

接收一个初始状态值,返回一个数组,包含当前状态值和更新状态值的方法。可以通过调用更新方法来改变状态值,并触发组件的重新渲染

//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];
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

情仇皆在手

暂无简介

0 文章
0 评论
23 人气
更多

推荐作者

13886483628

文章 0 评论 0

流年已逝

文章 0 评论 0

℡寂寞咖啡

文章 0 评论 0

笑看君怀她人

文章 0 评论 0

wkeithbarry

文章 0 评论 0

素手挽清风

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文