React严格模式下useReducer与useState行为差异及原理咨询
问题解析:Strict Mode下useReducer与useState的行为差异
1. 为什么useReducer会翻倍计数,而useState不会?
核心原因是你的reducer函数是不纯的:你直接修改了传入的state对象本身(state.count = state.count +1),而不是返回一个全新的状态对象。
React严格模式在开发环境中会重复调用reducer函数两次,目的是检测不纯操作(比如状态突变、副作用)。对于纯函数来说,两次调用的输入相同,输出也相同,不会影响最终状态;但你的reducer修改了原始state,第一次调用后state已经变成{count:1},第二次调用时基于这个已修改的state再递增,最终返回{count:2},导致界面显示翻倍。
而useState的更新逻辑是设计为纯操作的:
- 如果你使用函数式更新(
setCount(prev => prev+1)),即使严格模式下该函数被调用两次,因为它是纯函数(仅基于输入返回新值,不修改原始状态),两次调用的结果都是prev+1,React会忽略重复的结果,最终状态只递增1次。 - 如果你直接用
setCount(count+1),严格模式下虽然渲染会执行两次,但React会对重复的状态更新进行去重,最终只会应用一次更新,所以计数不会翻倍。
2. useReducer在Strict Mode下的底层机制
在开发环境的严格模式中:
- 每次
dispatch触发时,React会调用reducer函数两次,但只会保留第一次调用的结果来更新状态。 - 这个机制是为了强制开发者写出纯reducer:纯reducer不依赖外部状态、不修改输入参数、相同输入永远返回相同输出。如果reducer不纯(比如突变输入state),两次调用的结果会不一致,就能暴露潜在问题。
- 生产环境中,严格模式会被禁用,reducer只会被调用一次,不会有翻倍问题,但不纯的reducer依然可能导致难以追踪的bug(比如状态共享导致的意外更新)。
3. 两种reducer写法的差异原因
错误写法(当前代码):
const reducer = (state, action) => { if (action.type === "INCREMENT") { state.count = state.count + 1; // 直接修改原始state对象(不纯操作) return { count: state.count }; } return state; };
这里直接修改了传入的state参数,属于突变操作。严格模式下两次调用reducer时,第一次调用已经改变了原始state,第二次调用基于改变后的state再递增,导致最终计数翻倍。
正确写法(纯函数):
const reducer = (state, action) => { if (action.type === "INCREMENT") { return { count: state.count + 1 }; // 返回全新的状态对象(纯函数操作) } return state; };
这个reducer没有修改任何输入参数,而是返回一个全新的状态对象。严格模式下两次调用时,输入的state都是初始的{count:0},两次返回的结果都是{count:1},React会忽略第二次的重复结果,最终状态只递增1次,不管严格模式是否开启,行为都一致。
修复后的代码
将reducer改为纯函数写法即可解决问题:
import { useState, useReducer } from "react"; import styles from "./App.module.css"; const reducer = (state, action) => { if (action.type === "INCREMENT") { return { count: state.count + 1 }; // 纯函数写法,不修改原始state } return state; }; function App() { const [state, dispatch] = useReducer(reducer, { count: 0 }); const increment = () => { dispatch({ type: "INCREMENT" }); }; return ( <div> <button onClick={increment}>+</button> {state.count} <button>-</button> </div> ); } export default App;
内容的提问来源于stack exchange,提问作者Puneet Garg
相关产品推荐
相关产品推荐

