You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React StrictMode下嵌套状态更新异常及最佳实践咨询

React StrictMode下嵌套状态更新的异常问题解析

示例代码

import { useState } from "react";

export default function Clock() {
  const [x, setX] = useState(0);
  const [y, setY] = useState(0);

  const onClick = () => {
    setX((prevX) => {
      setY((prevY) => prevY + 1);
      return prevX + 1;
    });
  };

  return (
    <div>
      <button onClick={onClick}>Click to increase x and y</button>
      <div>x:{x}</div>
      <div>y:{y}</div>
    </div>
  );
}

疑问

  1. 已知React在StrictMode中会双重调用状态更新函数,但为何仅y递增2而x不会?且该现象为何不在首次点击时出现?
  2. 可以通过不嵌套状态更新调用来修复代码,但是否应始终避免嵌套状态更新?若要使用嵌套写法该如何正确编写?

解答

针对问题1

StrictMode在开发环境下会双重调用状态更新的回调函数,核心问题出在你的setX回调不是纯函数(里面嵌套了setY这个副作用操作),导致了异常:

  • x不递增2的原因:React会忽略setX回调第一次调用的返回值,只应用第二次的结果。两次调用里return prevX + 1的prevX都是当前的旧值(比如第二次点击时prevX是1),所以最终x只会加1。
  • y递增2的原因:setX的回调被调用两次,每次都会执行一次setY的函数式更新。因为setY的回调是纯函数,两次调用都会基于当前的旧y值加1,所以y会被累加两次。
  • 首次点击无异常的原因:首次点击时初始状态是x=0、y=0。第一次调用setX回调时,setY把y从0变成1,setX返回1,但React会回滚这次更新(x回到0,y回到0);第二次调用回调时,setY再次把y从0变成1,setX返回1,这次更新生效,所以最终x和y都是1,看起来正常。后续点击时,React不会回滚setY的两次更新,因此y会每次加2。

针对问题2

  • 是否要避免嵌套状态更新:是的,强烈建议避免。因为React要求状态更新的回调必须是纯函数——只能根据旧状态计算新状态,不能包含任何副作用(比如嵌套调用其他setState)。嵌套写法会破坏这个规则,不仅在StrictMode下出问题,还可能导致状态更新顺序混乱、难以调试。
  • 不存在“正确的嵌套写法”:React的设计逻辑里,状态更新回调就不应该有副作用。如果需要一次操作更新多个状态,正确的做法是在事件处理函数里分开调用状态更新,确保每个更新的回调都是纯函数:
    const onClick = () => {
      setX(prevX => prevX + 1);
      setY(prevY => prevY + 1);
    };
    
    如果多个状态之间有依赖关系(比如新的y值依赖x的新值),推荐使用useReducer来集中管理状态更新逻辑,这比嵌套setState更清晰、更可控。

内容的提问来源于stack exchange,提问作者gr8

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 14:43:29