如何避免独立组件同时触发重复数据请求?
解决Redux中同一数据多组件重复请求的问题
你的问题核心在于:两个组件同时发起请求时,Redux状态的更新是同步但有微小延迟的,第二个请求检查状态时,第一个请求的setRequest还未完成状态更新,导致重复请求。以下是几种可行的解决方案:
方案一:使用Redux Toolkit的createAsyncThunk(推荐)
Redux官方推荐的Redux Toolkit(RTK)内置了异步请求的状态管理,能自动处理请求的pending状态,且支持在发起前判断是否需要重复请求:
1. 定义Data Slice
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; import { get } from './你的API工具'; // 定义异步Thunk,添加请求前置判断 export const fetchData = createAsyncThunk( 'data/fetchData', async (url) => { const res = await get(url); return res.data; }, { condition: (url, { getState }) => { const { dataReducer } = getState(); // 检查该URL是否已有请求在处理中 const isPending = dataReducer.pendingRequests.includes(url); if (isPending) return false; // 已有请求,终止当前请求 } } ); const dataSlice = createSlice({ name: 'data', initialState: { data: {}, // 存储各URL对应的数据 pendingRequests: [] // 记录正在请求的URL }, extraReducers: (builder) => { builder .addCase(fetchData.pending, (state, action) => { state.pendingRequests.push(action.meta.arg); }) .addCase(fetchData.fulfilled, (state, action) => { const url = action.meta.arg; state.data[url] = action.payload; state.pendingRequests = state.pendingRequests.filter(u => u !== url); }) .addCase(fetchData.rejected, (state, action) => { const url = action.meta.arg; state.pendingRequests = state.pendingRequests.filter(u => u !== url); }); } }); export default dataSlice.reducer;
2. 组件中使用
import { useDispatch, useSelector } from 'react-redux'; import { fetchData } from './dataSlice'; import { useEffect } from 'react'; function TargetComponent({ url }) { const dispatch = useDispatch(); const data = useSelector(state => state.dataReducer.data[url]); const isLoading = useSelector(state => state.dataReducer.pendingRequests.includes(url)); useEffect(() => { if (!data && !isLoading) { dispatch(fetchData(url)); } }, [url, data, isLoading, dispatch]); // 渲染逻辑 if (isLoading) return <div>加载中...</div>; return <div>{JSON.stringify(data)}</div>; }
方案二:手动封装内存锁(无需RTK)
如果不使用RTK,可以用内存中的Set做即时请求锁(同步更新,比Redux状态更快),结合Redux状态处理页面刷新后的情况:
1. 封装请求Thunk
import { get } from './你的API工具'; // 内存中存储正在请求的URL,同步更新 const pendingRequests = new Set(); export function fetchData(url) { return async (dispatch, getState) => { // 先检查内存锁,避免同时发起的请求 if (pendingRequests.has(url)) return; // 再检查Redux状态,防止页面刷新后内存锁丢失 const state = getState(); if (state.dataReducer.pendingRequests.includes(url)) return; pendingRequests.add(url); dispatch({ type: 'SET_REQUEST', payload: url }); try { const res = await get(url); dispatch({ type: 'SET_DATA', payload: { url, data: res.data } }); } catch (err) { dispatch({ type: 'SET_ERROR', payload: { url, error: err } }); } finally { pendingRequests.delete(url); dispatch({ type: 'UNSET_REQUEST', payload: url }); } }; }
2. 组件中调用
useEffect(() => { dispatch(fetchData(url)); }, [url, dispatch]);
方案三:封装自定义Hook统一处理
将数据请求逻辑封装成自定义Hook,结合useRef记录已发起请求的状态,避免同一URL重复触发:
import { useDispatch, useSelector } from 'react-redux'; import { useEffect, useRef } from 'react'; import { fetchData } from './你的action'; export function useData(url) { const dispatch = useDispatch(); const data = useSelector(state => state.dataReducer.data[url]); const isLoading = useSelector(state => state.dataReducer.pendingRequests.includes(url)); const hasInitiated = useRef(false); useEffect(() => { if (!hasInitiated.current && !data && !isLoading) { hasInitiated.current = true; dispatch(fetchData(url)); } }, [url, data, isLoading, dispatch]); return { data, isLoading }; }
组件中直接使用:
const { data, isLoading } = useData(targetUrl);
内容的提问来源于stack exchange,提问作者ddominoo
相关产品推荐
相关产品推荐

