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

React应用中Redux Toolkit页面加载与切换时重复Dispatch问题解决

React应用Redux重复Dispatch问题修复

问题描述

React多页面应用中,用户进入page3时通过useEffect触发Redux异步请求同步数据,但在应用启动、page3加载及页面切换时,Redux Toolkit会触发两次Dispatch,导致重复发送请求。

相关代码

App.tsx

import { NavLink, Route, Routes } from "react-router-dom";
import "./styles.css";
import Todo from "./Todo";

const App = () => {
  const routes = [
    {
      path: "/",
      element: <div className="page1">Page 1</div>
    },
    {
      path: "/page2",
      element: <div className="page2">Page 2</div>
    },
    {
      path: "/page3",
      element: <Todo />
    }
  ];

  return (
    <div className="App">
      <div className="Navigation">
        <NavLink to="/">Page 1</NavLink>
        <NavLink to="/page2">Page 2</NavLink>
        <NavLink to="/page3">This sends a request</NavLink>
      </div>
      <div className="Content">
        <h2>Content</h2>
        <Routes>
          {routes.map((route: any) => (
            <Route path={route.path} element={route.element} key={route.path} />
          ))}
        </Routes>
      </div>
    </div>
  );
};

export default App;

Todo组件

import { useEffect } from "react";
import { useDispatch } from "react-redux";
import { AppDispatch } from "./store/store";
import { fetchTodo } from "./store/TodoSlice";

const Todo: React.FC = () => {
  const dispatch = useDispatch<AppDispatch>();

  useEffect(() => {
    dispatch(fetchTodo());
  }, []);

  return <div className="page3">This dispatches twice</div>;
};

export default Todo;

Store Slice

import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";

export interface ICategoryState {
  todo: any[];
  isLoading: boolean;
}

const initialState: ICategoryState = {
  todo: [],
  isLoading: false
};

const BASE_URL = "https://jsonplaceholder.typicode.com/todos";

export const fetchTodo = createAsyncThunk("todos/fetchTodo", async () => {
  const response = await fetch(BASE_URL);
  const data = await response.json();
  return data as any[];
});

export const todoSlice = createSlice({
  name: "todos",
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder.addCase(fetchTodo.pending, (state) => {
      state.isLoading = true;
      console.log("Start loading");
    });
    builder.addCase(fetchTodo.fulfilled, (state, { payload }) => {
      state.todo = payload;
      state.isLoading = false;
    });
    builder.addCase(fetchTodo.rejected, (state) => {
      console.log("Error happened");
      state.isLoading = false;
    });
  }
});

export default todoSlice.reducer;

问题原因

开发环境下React的StrictMode会重复执行副作用(如useEffect)、状态初始化等逻辑,目的是检测潜在的副作用问题。这导致useEffect中的dispatch(fetchTodo())被执行两次,进而触发两次请求。生产环境中StrictMode不会生效,不会出现此问题,但开发阶段需要处理重复请求的问题。

修复方案

方案1:利用Redux状态拦截重复请求

在异步Thunk中通过Redux现有状态判断,避免重复发起请求:

export const fetchTodo = createAsyncThunk("todos/fetchTodo", async (_, { getState }) => {
  const { todos } = getState() as { todos: ICategoryState };
  // 若正在加载或已有数据,直接返回现有数据
  if (todos.isLoading || todos.todo.length > 0) {
    return todos.todo;
  }
  const response = await fetch(BASE_URL);
  const data = await response.json();
  return data as any[];
});

方案2:组件内添加请求条件判断

在Todo组件中通过useSelector获取Redux状态,仅在无数据且未加载时发起请求:

import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { AppDispatch, RootState } from "./store/store"; // 需定义RootState
import { fetchTodo } from "./store/TodoSlice";

const Todo: React.FC = () => {
  const dispatch = useDispatch<AppDispatch>();
  const { isLoading, todo } = useSelector((state: RootState) => state.todos);

  useEffect(() => {
    if (!isLoading && todo.length === 0) {
      dispatch(fetchTodo());
    }
  }, [dispatch, isLoading, todo]);

  return <div className="page3">已修复重复请求</div>;
};

export default Todo;

方案3:临时移除StrictMode(不推荐长期使用)

在项目入口文件(如index.tsx)中移除<React.StrictMode>的包裹,这样开发环境下不会重复执行副作用。但此方案会丢失StrictMode的副作用检测能力,仅适合临时验证问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 18:27:31