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> ); }
疑问
- 已知React在StrictMode中会双重调用状态更新函数,但为何仅y递增2而x不会?且该现象为何不在首次点击时出现?
- 可以通过不嵌套状态更新调用来修复代码,但是否应始终避免嵌套状态更新?若要使用嵌套写法该如何正确编写?
解答
针对问题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的设计逻辑里,状态更新回调就不应该有副作用。如果需要一次操作更新多个状态,正确的做法是在事件处理函数里分开调用状态更新,确保每个更新的回调都是纯函数:
如果多个状态之间有依赖关系(比如新的y值依赖x的新值),推荐使用const onClick = () => { setX(prevX => prevX + 1); setY(prevY => prevY + 1); };useReducer来集中管理状态更新逻辑,这比嵌套setState更清晰、更可控。
内容的提问来源于stack exchange,提问作者gr8
相关产品推荐
相关产品推荐

