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

如何避免useEffect中dispatch多个Redux异步Action重复发起网络请求?

解决Redux createAsyncThunk重复网络请求问题

你的代码中,Redux的useDispatch返回的dispatch是稳定引用,理论上依赖[dispatch]的useEffect应该只在组件挂载时执行一次。出现多次触发的原因通常是组件被重复挂载/卸载,或者父组件状态变化导致不必要的重渲染。以下是几种可行的解决方法:

方法一:优化组件渲染,避免不必要重渲染

用React.memo包裹组件,通过浅比较props来阻止父组件无关状态变化引发的子组件重复渲染:

import { memo } from 'react';
import { useDispatch } from 'react-redux';

const Dashboard = memo(() => {
  const dispatch = useDispatch();

  useEffect(() => {
    function initDashboardData() {
      console.count("initDashboardData");
      void dispatch(getDatabasesList());
      void dispatch(getUserConfig());
      void dispatch(
        getHistoricalData({ startTimestamp: getDateXDaysAgo(14, new Date()), endTimestamp: new Date() })
      );
    }

    void initDashboardData();
  }, [dispatch]);

  // 组件渲染内容
});

export default Dashboard;

方法二:在Redux层面拦截重复请求

在createAsyncThunk内部结合slice的加载状态,判断是否已有正在进行的请求,避免重复调用API:

// 对应slice的状态定义
const initialState = {
  databasesList: [],
  isDatabasesLoading: false,
  userConfig: null,
  isUserConfigLoading: false,
  historicalData: null,
  isHistoricalDataLoading: false,
};

const dashboardSlice = createSlice({
  name: 'dashboard',
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder
      // 处理getDatabasesList的状态
      .addCase(getDatabasesList.pending, (state) => {
        state.isDatabasesLoading = true;
      })
      .addCase(getDatabasesList.fulfilled, (state, action) => {
        state.databasesList = action.payload;
        state.isDatabasesLoading = false;
      })
      .addCase(getDatabasesList.rejected, (state) => {
        state.isDatabasesLoading = false;
      })
      // 同理处理getUserConfig的状态
      .addCase(getUserConfig.pending, (state) => {
        state.isUserConfigLoading = true;
      })
      .addCase(getUserConfig.fulfilled, (state, action) => {
        state.userConfig = action.payload;
        state.isUserConfigLoading = false;
      })
      .addCase(getUserConfig.rejected, (state) => {
        state.isUserConfigLoading = false;
      });
  },
});

// 修改createAsyncThunk逻辑,添加重复请求判断
export const getDatabasesList = createAsyncThunk(
  'dashboard/getDatabasesList',
  async (_, { getState }) => {
    const { dashboard } = getState();
    // 如果正在加载,直接返回,不发起新请求
    if (dashboard.isDatabasesLoading) return;
    const response = await api.get('/databases');
    return response.data;
  }
);

// getUserConfig做同样的修改
export const getUserConfig = createAsyncThunk(
  'dashboard/getUserConfig',
  async (_, { getState }) => {
    const { dashboard } = getState();
    if (dashboard.isUserConfigLoading) return;
    const response = await api.get('/user-config');
    return response.data;
  }
);

方法三:用Ref标记初始化状态

在组件内部通过useRef存储标记,确保初始化逻辑只执行一次:

import { useRef, useEffect } from 'react';
import { useDispatch } from 'react-redux';

const Dashboard = () => {
  const dispatch = useDispatch();
  const hasInitialized = useRef(false);

  useEffect(() => {
    if (!hasInitialized.current) {
      const initDashboardData = () => {
        console.count("initDashboardData");
        void dispatch(getDatabasesList());
        void dispatch(getUserConfig());
        void dispatch(
          getHistoricalData({ startTimestamp: getDateXDaysAgo(14, new Date()), endTimestamp: new Date() })
        );
      };

      initDashboardData();
      hasInitialized.current = true;
    }
  }, [dispatch]);

  // 组件渲染内容
};

该方法强制初始化逻辑仅执行一次,即使组件重复渲染也不会再次触发。若组件卸载后重新挂载,ref会重置,此时会再次执行初始化。

总结

优先通过React.memo优化组件渲染(方法一),从根源减少不必要的effect触发;若组件无法避免重复渲染,可选择用useRef标记(方法三);如果需要全局层面拦截重复请求,推荐在Redux的thunk逻辑中处理(方法二)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 20:15:29