React 状态更新机制详解 3之彻底理解

0 阅读3分钟

React 状态更新机制详解 3

React 状态更新机制详解

1. 代码执行阶段(同步执行)
setCount(count + 1); // ①
setCount(count + 1); // ②
setCount(count + 1); // ③
  • 此时内存中的 count 值始终为 0(初始值)
  • 三个 setCount() 调用都是基于当前的 count=0 计算:
    • ① → setCount(0 + 1) → 计划更新为 1
    • ② → setCount(0 + 1) → 计划更新为 1
    • ③ → setCount(0 + 1) → 计划更新为 1
  • 关键点:这些调用只是将更新请求加入队列,并不立即修改内存中的 count
2. React 内部更新队列处理(异步批处理)
// React 内部伪代码
function processUpdates() {
  // 检查更新队列
  const updates = [update1, update2, update3];

  // 对于直接值更新:
  let nextState = currentState; // 初始为0

  // 遍历所有更新请求
  for (const update of updates) {
    if (typeof update === "function") {
      // 函数式更新处理
      nextState = update(nextState);
    } else {
      // 直接值更新:总是使用最新的值覆盖
      nextState = update; // 这里三次都是 1
    }
  }

  // 最终只保留最后一个值
  commitStateChange(nextState); // 最终设置 count=1
}
3. 内存状态变化时间线
阶段内存中的 count说明
组件挂载0初始状态
用户点击触发事件处理函数0事件处理函数开始执行
执行 setCount(count+1) (第一次)0计划更新为 1
执行 setCount(count+1) (第二次)0计划更新为 1(覆盖前一个)
执行 setCount(count+1) (第三次)0计划更新为 1(覆盖前两个)
事件处理函数结束0所有 setCount 调用完成
React 开始处理更新队列0仍在当前渲染周期
React 应用最终更新→ 1更新到 DOM 和内存
组件重新渲染1新值生效

函数式更新的处理差异

当使用函数式更新时:

setCount((prev) => prev + 1); // ①
setCount((prev) => prev + 1); // ②
setCount((prev) => prev + 1); // ③

React 的处理方式不同:

function processUpdates() {
  let nextState = currentState; // 初始为0

  // 遍历所有更新函数
  nextState = update1(nextState); // 0 => 0+1=1
  nextState = update2(nextState); // 1 => 1+1=2
  nextState = update3(nextState); // 2 => 2+1=3

  commitStateChange(nextState); // 最终设置 count=3
}

核心要点总结

  1. 同步执行阶段

    • 所有 setCount() 调用都基于当前渲染周期的状态值(本例中始终为 0)
    • 内存中的状态值在此阶段不会改变
    • 只是将更新请求加入队列
  2. 异步更新阶段

    • React 在事件处理完成后统一处理更新队列
    • 对于直接值更新 (setCount(value)):
      • 多个更新会被合并
      • 只保留最后一个值(本例中三次都是 1)
    • 对于函数式更新 (setCount(updater)):
      • 按顺序执行每个更新函数
      • 每个函数接收前一个更新结果
  3. 内存状态变化时机

    • 只在 React 完成队列处理后改变
    • 在重新渲染前更新内存值
    • 更新后触发组件重新渲染

实验验证

function Counter() {
  const [count, setCount] = useState(0);

  const handleDirectUpdate = () => {
    console.log("【直接更新开始】内存count=", count);

    setCount(count + 1);
    console.log("更新1计划后 - 内存count=", count);

    setCount(count + 1);
    console.log("更新2计划后 - 内存count=", count);

    setCount(count + 1);
    console.log("更新3计划后 - 内存count=", count);
  };

  console.log("渲染发生,当前count=", count);

  return (
    <div>
      <button onClick={handleDirectUpdate}>测试直接更新 (预期错误)</button>
    </div>
  );
}

输出结果

渲染发生,当前count= 0
【直接更新开始】内存count= 0
更新1计划后 - 内存count= 0
更新2计划后 - 内存count= 0
更新3计划后 - 内存count= 0
渲染发生,当前count= 1  (React处理更新后)

这个实验证明了这个理解:在同步执行阶段,内存中的状态值始终保持不变,直到 React 异步处理完所有更新请求。