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

React严格模式下如何避免useReducer搭配useEffect触发两次?

React Strict Mode下useEffect触发两次导致useReducer幂等性失效的解决办法

React 18的Strict Mode在开发环境中会重复执行useEffect等钩子,目的是检测代码中的非幂等副作用。你遇到的问题是:用useState时可以通过状态判断实现幂等,但useReducer的dispatch会在两次useEffect执行完成后才批量生效,导致状态被更新两次。

问题差异分析

useState示例为什么有效

你的useState代码中,useEffect依赖了counter状态:

  • 第一次useEffect触发时,counter为0,执行setCounter(1),状态更新后组件重新渲染。
  • Strict Mode触发第二次useEffect时,counter已经是1,不满足counter === 0的条件,所以不会重复更新状态,最终结果为1,符合预期。

useReducer示例为什么失效

你的useReducer代码中,useEffect没有添加依赖项:

  • 两次useEffect触发时,都读取到初始的state.counter = 0,因此两次都执行了dispatch({ type: "increment" })。
  • React在Strict Mode下会推迟dispatch的执行(批量更新机制),两次dispatch最终都生效,导致counter变成2,幂等性失效。

解决办法

方法1:给useEffect添加正确的依赖项

将state.counter加入useEffect的依赖数组,这样第二次执行useEffect时,状态已经更新,条件不满足就不会重复dispatch:

const { StrictMode, useEffect, useReducer } = React;

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <StrictMode>
    <WithUseReducer />
  </StrictMode>
);

function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { ...state, counter: state.counter + 1 };
    default:
      throw new Error();
  }
}

function WithUseReducer() {
  const [state, dispatch] = useReducer(reducer, { counter: 0 });

  useEffect(() => {
    if (state.counter === 0) {
      dispatch({ type: "increment" });
    }
  }, [state.counter]); // 添加state.counter作为依赖

  return <React.Fragment>{state.counter}</React.Fragment>;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>

方法2:使用ref标记执行状态

通过useRef创建一个标记,确保副作用只执行一次,不受Strict Mode重复触发的影响:

const { StrictMode, useEffect, useReducer, useRef } = React;

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <StrictMode>
    <WithUseReducer />
  </StrictMode>
);

function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { ...state, counter: state.counter + 1 };
    default:
      throw new Error();
  }
}

function WithUseReducer() {
  const [state, dispatch] = useReducer(reducer, { counter: 0 });
  const hasExecuted = useRef(false);

  useEffect(() => {
    if (!hasExecuted.current && state.counter === 0) {
      dispatch({ type: "increment" });
      hasExecuted.current = true;
    }
  }, [state.counter]);

  return <React.Fragment>{state.counter}</React.Fragment>;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>

注意事项

Strict Mode的重复执行是开发环境独有的行为,生产环境不会出现。它的目的是帮你提前发现副作用中的非幂等问题,所以最好的实践是让你的副作用本身具备幂等性,而不是单纯规避重复执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 01:30:47