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

React+Redux Toolkit如何避免同一组件多处渲染重复发API请求

问题原因

两个Navbar组件几乎同时挂载时,内部的useEffect会并发触发dispatch(fetchNavigation),此时第一次请求还未完成,state中的loaded字段仍为初始的false,因此两次dispatch都会触发网络请求,你之前的方案未生效就是因为没有处理请求进行中的状态。

解决方案

方案1:改造thunk添加前置校验(推荐,组件逻辑闭环)

1. 先完善navigation slice的状态,新增loading字段处理请求中状态

import { createSlice } from "@reduxjs/toolkit";
import { fetchNavigation } from "../services/fetchNavigation"

const initialState = {
    "count": 0,
    "next": null,
    "previous": null,
    "navs": [],
    "loaded": false,
    "loading": false // 新增:标记请求是否正在进行
}

const navSlice = createSlice({
    name: 'navigation',
    initialState: initialState,
    extraReducers: {
        // 新增pending状态处理
        [fetchNavigation.pending]: (state) => {
            state.loading = true
        },
        [fetchNavigation.fulfilled]: (state, action) => {
            state.count = action.payload.count;
            state.next = action.payload.next;
            state.previous = action.payload.previous;
            state.loaded = true
            state.navs = action.payload.results;
            state.loading = false // 请求完成重置loading
        },
        // 新增rejected状态处理,避免请求失败后永远处于loading
        [fetchNavigation.rejected]: (state) => {
            state.loading = false
        }
    },
})

export default navSlice.reducer

2. 改造fetchNavigation异步action,发起请求前先校验状态

利用createAsyncThunk payloadCreator的第二个参数拿到全局state,判断如果已经加载完成、或者正在加载中,就直接终止请求逻辑:

import { createAsyncThunk } from "@reduxjs/toolkit";
import { instance } from "../../API/config"

export const fetchNavigation = createAsyncThunk(
    "navigation/fetchNavigation", 
    async (_, { getState, rejectWithValue }) => {
        // 先从state中拿到navigation模块的状态
        const { navigation } = getState()
        // 已经加载完成 或者 正在加载中,都不发起新请求
        if (navigation.loaded || navigation.loading) {
            return rejectWithValue('无需重复请求导航数据')
        }
        try {
            const response = await instance.get(`nav/`)
            return response.data
        } catch (err) {
            return rejectWithValue(err.message)
        }
    }
);

3. (可选双重保险)组件内dispatch前加判断

也可以在Navbar组件的useEffect里先判断状态,再决定是否dispatch:

useEffect(() => {
    // 只有未加载、也不在加载中时才发起请求
    if (!navigation.loaded && !navigation.loading) {
        dispatch(fetchNavigation());
    }
}, [dispatch, navigation.loaded, navigation.loading])

方案2:请求逻辑上移到公共父组件

如果你的两个Navbar有共同的父组件(比如全局Layout组件、App根组件),可以直接把dispatch(fetchNavigation)的逻辑移到父组件的useEffect里执行,两个Navbar组件删掉内部的dispatch逻辑,从源头保证只会触发一次请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 22:09:03