React useState 的懒初始化机制

前言

在 React 中,useState 可以接收两种类型的参数:

  1. 普通初始值

    scss 复制代码
    const [state, setState] = useState(0);
  2. 懒初始化函数

    scss 复制代码
    const [state, setState] = useState(() => computeExpensiveValue());

懒初始化函数的核心目标是:避免在非首次渲染时执行昂贵的初始计算。


1. 懒初始化函数的行为

用一个例子对比两种写法:

scss 复制代码
function expensiveInit() {
  console.log('计算初始值');
  return 42;
}

// 普通方式
const [count1, setCount1] = useState(expensiveInit());

// 懒初始化方式
const [count2, setCount2] = useState(() => expensiveInit());

输出顺序:

  • 普通方式:计算初始值 每次渲染都会打印
  • 懒初始化方式:计算初始值 只在首次渲染打印一次

为什么会这样?我们需要看 React 的底层工作机制。


2. React 内部是如何处理的

React 使用 Fiber 架构 来管理函数组件的状态。每个组件对应一个 Fiber 节点,useState 通过 hook 链表存储状态信息。

2.1 useState 底层步骤

  1. 创建或获取 hook 对象

    每次调用 useState,React 会检查当前 Fiber 的 hook 链表是否已经存在对应的 hook:

    • 首次渲染:创建 hook 对象,存储状态和更新队列
    • 更新渲染:直接从 hook 链表读取上一次的状态
  2. 判断传入参数类型

    • 如果传入 函数,React 会 判断是否是第一次执行:

      ini 复制代码
      if (typeof initialState === 'function') {
        hook.memoizedState = initialState(); // 仅首次渲染执行
      } else {
        hook.memoizedState = initialState;   // 直接使用值
      }
    • 如果是普通值,直接赋值给 hook.memoizedState。

  3. 更新机制

    当 setState 被调用时,React 会把新的状态加入更新队列,然后调度重新渲染:

    • 再次渲染时 不会再次执行懒初始化函数
    • React 会直接使用 hook 中存储的状态或更新队列计算的新状态

2.2 Fiber 链表与 hook 链表

每个函数组件的状态存储在一个 hook 链表 中:

rust 复制代码
FiberNode
 ├── memoizedState -> Hook (useState)
 ├── memoizedState -> Hook (useState)
 └── ...

每个 Hook 对象结构大概如下:

yaml 复制代码
{
  memoizedState: 初始状态或最新状态,
  queue: 状态更新队列,
  next: 下一个 Hook
}
  • 首次渲染:

    • React 会遍历 hook 链表,创建每个 hook 对象
    • 如果 hook 接收的是函数,React 会立即执行它来获得初始状态
  • 更新渲染:

    • React 直接读取 hook 链表里的状态
    • 传给 useState 的函数 不会再执行

3. 为什么使用懒初始化

假设我们有一个复杂计算:

bash 复制代码
function expensiveInit() {
  let sum = 0;
  for (let i = 0; i < 1e8; i++) sum += i;
  return sum;
}
  • 如果写成普通方式:

    scss 复制代码
    const [value, setValue] = useState(expensiveInit());

    每次渲染都会执行这个循环,导致性能严重下降。

  • 如果使用懒初始化:

    scss 复制代码
    const [value, setValue] = useState(() => expensiveInit());

    循环只在首次渲染执行一次,后续渲染直接复用状态。


4. 总结

所谓的懒初始化 是通过向 useState 传入函数,使得初始化逻辑只在首次渲染时由 React 调用,避免组件每次执行时都提前计算初始值,从而减少不必要的性能开销。(懒初始化优化的不是 React 的行为,而是 JS 求值时机。)

相关推荐
记得开心一点嘛17 小时前
Trellora:基于 Electron、React 和本地知识库的 AI 知识工作台
人工智能·react.js·electron
5008418 小时前
React Native for OpenHarmony 实战:三方库 react-native-url-polyfill 的鸿蒙化适配指南
javascript·react native·react.js·性能优化·electron·harmonyos
500841 天前
React Native for OpenHarmony 实战:三方库 react-native-volume-control 的鸿蒙化适配指南
javascript·react native·react.js·electron·harmonyos
薛一半1 天前
React-Redux三重优化实战揭秘
javascript·vue.js·react.js
薛一半3 天前
React-Redux核心原理与容器组件实战
前端·javascript·react.js
FungLeo3 天前
成为全栈·Next.js 网站前台篇·评论系统:叠楼、回复、删除与内容审核如何落到前台
react.js·内容审核·next.js·评论系统·树形数据·成为全栈
Alice-YUE3 天前
React 渲染机制速通:Render 树、Fiber 树与更新调度
前端·react.js·前端框架·react·fiber·前端性能·渲染机制
liangshanbo12154 天前
React Fiber:从“解决卡顿”到完整更新链路
前端·javascript·react.js
ss2735 天前
AI全栈实战 | 2.2-02 React 思维模型:数据不可变 vs Vue 数据可变,两种哲学把复杂度推给谁
前端·vue.js·react.js