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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 08:00:54