Yasin

Yasin

为什么说懒初始化是效率的?

这是 React 内部机制(Hook 的工作原理)决定的。要理解这个原理,我们需要把视角从“我在写代码”转换到“React 实际上在怎么运行我的代码”。

核心原理:React 如何处理 Hook 的链表

在 React 内部,每个组件实例都维护着一个 Hook 链表(Linked List)

1. 首次渲染(Mount)

当组件第一次被调用时:

  1. React 创建了一个空的 Hook 链表。
  2. 遇到 useState(initFn)
  3. React 发现这是第一次,所以它必须算出初始值。
  4. 它看到传入的是一个函数 initFn,于是执行该函数 val = initFn()
  5. React 将结果 val 存入当前 Hook 的节点(MemoizedState)中。
  6. 返回 [val, dispatch]

2. 再次渲染(Re-render)

当组件因为 props 变了或 setState 被调用而再次执行时:

  1. React 复用之前的 Hook 链表。
  2. 再次遇到 useState(initFn)
  3. React 查看当前 Hook 节点,发现:“噢,这里已经存过状态了!
  4. React 直接忽略传入的参数(不管是值还是函数 initFn)。它根本不会去调用 initFn
  5. 它直接从链表中取出之前存好的(或者是最新更新过的)state
  6. 返回 [currentState, dispatch]

用伪代码模拟 React 内部行为

我们可以写一段简化版的 JS 代码来模拟 React useState 的内部逻辑,你会立刻明白为什么函数只跑一次:

// React 内部维护的全局变量(简化版)
let hookState = []; // 存放每个 hook 的状态
let hookIndex = 0; // 当前执行到第几个 hook

function useState(initialState) {
  const currentIndex = hookIndex;
  hookIndex++; //哪怕后面不执行,索引也要加1

  // 关键判断:即便你传入了函数,只要状态已存在,我就不看它
  if (hookState[currentIndex] !== undefined) {
    // 【Re-render 路径】
    // 直接返回之前存好的值,完全不管 initialState 参数是什么
    return [hookState[currentIndex], setState];
  }

  // 【Mount 路径 - 只有第一次会进这里】
  let value;
  if (typeof initialState === "function") {
    // 只有这里才会调用你的函数!
    value = initialState();
  } else {
    value = initialState;
  }

  hookState[currentIndex] = value;

  function setState(newVal) {
    /* ...触发重渲染... */
  }

  return [value, setState];
}

总结

原理非常简单粗暴:

  1. Mount 阶段:React 需要初始值 -> 不得不执行你的函数。
  2. Update 阶段:React 已经有值了 -> 直接丢弃你传入的参数(参数虽然被传入了函数,但 React 内部逻辑里的 if 判断让它根本没机会被调用)。

这就是为什么惰性初始化能节省性能:虽然函数定义本身在每次渲染时都会重新创建(因为在组件函数体内),但那个函数体内部的代码永远不会被执行。