0%

已完成 0/29 关

教学模式

你可以随时调整。

此设置调整教师的教学方式,无需重新生成课程。

第 17 关 含代码 含表格 约 8 分钟

状态更新队列:批处理机制与更新函数 updater

状态更新队列:批处理机制与更新函数 updater

在上一关中,你发现连续写三次 setNumber(number + 1) 最终只增加了 1,因为每次调用的都是当次快照里的同一个数字。

但如果你真的需要在一个点击事件里多次累加状态(比如点击一次完成三道连击计算,或者连续分步修改同一状态),该怎么做呢?

答案是:使用更新函数(Updater Function),并借助 React 底层的状态更新队列(Queueing)。


1. 餐厅服务员的“批处理(Batching)”艺术

在解释更新函数之前,先理解 React 是如何处理状态更新请求的。

想象你坐在餐厅里:

  • 你对服务员说:“给我来一份沙拉”;
  • 还没等服务员转身,你紧接着说:“再加一份牛排”;
  • 紧接着又说:“还有一杯红酒”。

服务员会每次一听你说完一个单词,就立刻风风火火跑回后厨下单一次,一共来回跑三次吗? 当然不会! 服务员会耐心等你把整句话说完,一次性把菜单记在小本本上,然后才走进后厨统一交给厨师。

React 也是这样工作的!在 React 中,直到事件处理函数内部的所有同步代码全部执行完毕,React 才会统一着手进行重新渲染。这一特性被称为 批处理(Batching)。 批处理极大地保护了应用的性能,避免了用户每点一次按钮,整个页面就疯狂抖动重新渲染三四次。


2. 使用更新函数(Updater Function)

如果你希望在下一次渲染之前,基于排队中的前一个更新结果继续计算,你可以向 set 函数传递一个以旧状态为参数、返回新状态的函数,而不是传递一个死的值:

import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(n => n + 1);
        setNumber(n => n + 1);
        setNumber(n => n + 1);
      }}>+3</button>
    </>
  );
}

现在,点击一次按钮,数字如你所愿,稳稳地变成了 3!


3. 深入底层:React 是如何演算更新队列的?

这里传递的 n => n + 1 被称为 更新函数(Updater Function)。 (约定俗成使用该状态变量的首字母命名参数,比如 number 的首字母 n)。

当你写下上面这三行代码时,React 并不是立刻执行计算,而是把这三个函数放进了一个**待处理队列(Queue)**中:

队列排入的更新传入的值 / 函数待处理队列中的状态计算结果
第 1 次n => n + 1初始值 0 $
ightarrow$ 计算出 1
第 2 次n => n + 1带着上一轮的 1 $
ightarrow$ 计算出 2
第 3 次n => n + 1带着上一轮的 2 $
ightarrow$ 计算出 3

当点击事件函数的所有代码执行完毕后,React 会在后台清空这个队列,把最终算出来的 3 赋给下次渲染的 number,并启动一次界面重绘!


4. 烧脑推演:混合使用值与更新函数

让我们来看一个非常经典的进阶推演题。看看你能否推导出最终屏幕上会显示几?

<button onClick={() => {
  setNumber(number + 5);
  setNumber(n => n + 1);
  setNumber(42);
}}>

假设起始值 number 是 0。点击后:

  1. setNumber(number + 5): 当次快照中 number 是 0,所以这行等于 setNumber(0 + 5),React 把直接值 5 放入队列;
  2. setNumber(n => n + 1): React 发现这是一个更新函数。它读取队列中上一项的结果(5),带入计算得出 6 并压入队列;
  3. setNumber(42): React 发现这是一个直接值,它才不管之前算到了 6,直接把结果强制覆盖替换为 42!

事件结束,队列演算完成,最终界面显示的数字是:42!


总结

  • React 采用批处理(Batching),等事件函数的所有同步代码执行完后才进行一次统一重新渲染;
  • 当你需要基于前序排队状态连续多次累加更新时,传递更新函数 setState(prev => prev + 1);
  • 更新函数必须是纯函数,不能产生外部副作用;
  • 深刻理解队列演算,能让你在处理复杂、高频的状态流转时做到心明眼亮。
下一站:更新 State 中的对象:不可变性与展开语法复制 →
© 2026 React 官方教程