React严格模式下结合Map为何会触发两次状态更新?
问题原因解析
1. React StrictMode的核心行为
StrictMode的设计目的是检测代码中的潜在副作用,它会双重执行以下关键函数:
- 组件初始化函数
- useState的初始值生成函数
- useEffect的挂载回调
- setState的更新函数(即你传给
setCountdownTimers的箭头函数)
2. 你的代码中的关键问题
在setState的更新逻辑里,你直接修改了React传入的prevState这个Map实例:
for (const [timerKey, timer] of prevState) { prevState.set(timerKey, timer - 1); // 直接修改原状态引用 } return new Map(prevState);
Map是引用类型,React传递给更新函数的prevState是当前状态的实际内存引用,而非副本。当StrictMode双重执行这个更新函数时:
- 第一次执行:把
prevState中的值从60改成59,返回新Map - 第二次执行:此时
prevState已经是被修改后的59,再次减1变成58,返回新Map
两次修改的效果叠加,就导致每秒数值直接减2。
3. 为什么换成普通对象可能不会触发这个问题?
如果使用普通对象,正确的更新逻辑是创建新对象而非修改原对象:
setCountdownTimers(prev => { return { ...prev, [1]: prev[1] - 1 }; // 基于原对象生成新对象,不修改原引用 });
这种情况下,即使StrictMode双重执行更新函数,两次都是基于原始状态值计算,React会自动忽略重复的更新结果,最终只会生效一次。而你的Map代码直接修改了原引用,才导致两次修改都产生了实际影响。
4. 修复方案
不要直接修改prevState,而是先创建它的副本再修改:
setCountdownTimers((prevState) => { const newMap = new Map(prevState); // 先复制原Map生成新实例 for (const [timerKey, timer] of newMap) { newMap.set(timerKey, timer - 1); // 修改副本而非原状态 } return newMap; });
这样即使更新函数被执行两次,每次都是基于原始状态值计算,两次结果一致,React会自动合并为一次更新,数值就能正常每秒减1。
内容的提问来源于stack exchange,提问作者Plato
相关产品推荐
相关产品推荐

