首页 / React 19 入门教程 / State 作为快照

React 19 入门教程

State 作为快照

本教程共 50 篇 · 第 16 篇 · 更新于 2026-07-29 · 约 7 分钟阅读

React状态快照批处理函数式更新异步更新

16. State 作为快照

本节目标:理解状态在单次渲染中是不变的”快照”,掌握批处理和函数式更新,避免状态更新中的常见陷阱。

状态在渲染中是不变的

调用 setCount 不会立刻改变 count 的值。它请求 React 在下次渲染时使用新值:

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

  function handleClick() {
    setCount(count + 1);
    console.log(count); // 还是旧值!不是 +1 后的
  }

  return <button onClick={handleClick}>{count}</button>;
}

第一次点击,setCount(1) 被调用,但 console.log 打印的还是 0。

每次渲染中的状态是固定的快照,就像照片里的人不会突然变老。

批处理:多次 setState 合并

连续调用两次 setCount,不会渲染两次:

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

  function handleClick() {
    setCount(count + 1);
    setCount(count + 1);
    // count 从 0 变成 1,不是 2
  }

  return <button onClick={handleClick}>{count}</button>;
}

两次 setCount 都基于同一次渲染的快照(count = 0),所以结果还是 1。

什么是批处理

React 会把同一个事件处理函数里的多个 setState 合并成一次渲染。这是性能优化,避免不必要的重复渲染。

函数式更新:基于前一个状态

想让计数器连加两次?用函数式更新:

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

  function handleClick() {
    setCount(prev => prev + 1);
    setCount(prev => prev + 1);
    // count 从 0 变成 2
  }

  return <button onClick={handleClick}>{count}</button>;
}

setCount(prev => prev + 1) 里的 prev 是 React 保证的最新状态值。两次调用依次执行:0→1→2。

什么时候用函数式更新

新状态依赖旧状态时,一律用函数式更新。不依赖旧状态时,直接传值就行。

一个完整的例子

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

  function handleIncrement() {
    setCount(prev => prev + 1);
  }

  function handleDecrement() {
    setCount(prev => prev - 1);
  }

  function handleReset() {
    setCount(0); // 不依赖旧值,直接传
  }

  return (
    <div>
      <p>{count}</p>
      <button onClick={handleIncrement}>+1</button>
      <button onClick={handleDecrement}>-1</button>
      <button onClick={handleReset}>重置</button>
    </div>
  );
}

对象和数组的状态更新

直接修改对象不会触发渲染,必须传入新引用:

function Form() {
  const [person, setPerson] = useState({ name: '', age: 0 });

  // 错误:直接修改
  // person.name = '张三';

  // 正确:传入新对象
  setPerson({ ...person, name: '张三' });
}

不要直接修改状态

person.name = '张三' 不会触发渲染。React 通过引用比较判断状态是否变化,直接修改原对象引用不变。

状态更新的时间线

用户点击 → 事件处理函数执行 → 多个 setState 被调用
→ React 批处理,合并更新 → 重新渲染 → 页面更新

整个过程是异步的。setState 调用后,状态不会立即变化。

React 18+ 自动批处理

React 18 之前,只有事件处理函数里的 setState 会被批处理。Promise、setTimeout 里的不会。React 18 之后全部自动批处理。

本节要点

  • 单次渲染中状态是固定快照
  • 批处理合并多个 setState 为一次渲染
  • 新状态依赖旧状态时用函数式更新 prev => prev + 1
  • 不要直接修改对象/数组状态,要传入新引用