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
相关产品推荐
相关产品推荐

