Redux项目中如何获取组件调用的API列表与状态以控制加载器
解决方案:基于Redux追踪页面API请求状态控制加载Spinner
核心思路
要实现需求,核心是给每个API请求打上页面标识,通过Redux统一追踪请求状态,再在页面组件中筛选出当前页面的请求,判断是否存在pending状态,进而控制Spinner显示。
具体实现步骤
1. 为API请求添加页面元信息
在发起API请求的action中,携带当前页面标识和API路径:
// 示例action creator(以redux-thunk为例) export const fetchUserNames = () => ({ type: 'FETCH_USER_NAMES_PENDING', meta: { page: 'home', endpoint: '/userNames' } }); export const fetchVersions = () => ({ type: 'FETCH_VERSIONS_PENDING', meta: { page: 'home', endpoint: '/versions' } });
2. 在Reducer中维护请求状态
创建专门的reducer存储所有请求的状态、所属页面及API路径:
const initialState = { requests: {} // 结构:{ 唯一标识: { status: 'pending'|'success'|'failure', page, endpoint } } }; const requestReducer = (state = initialState, action) => { // 处理请求开始 if (action.type.endsWith('_PENDING')) { const requestKey = `${action.meta.page}-${action.meta.endpoint}`; return { ...state, requests: { ...state.requests, [requestKey]: { status: 'pending', page: action.meta.page, endpoint: action.meta.endpoint } } }; } // 处理请求成功/失败 if (action.type.endsWith('_SUCCESS') || action.type.endsWith('_FAILURE')) { // 从action类型中解析出对应的pending标识 const baseType = action.type.replace('_SUCCESS', '').replace('_FAILURE', ''); const pendingAction = { type: `${baseType}_PENDING`, meta: action.meta }; const requestKey = `${pendingAction.meta.page}-${pendingAction.meta.endpoint}`; return { ...state, requests: { ...state.requests, [requestKey]: { ...state.requests[requestKey], status: action.type.endsWith('_SUCCESS') ? 'success' : 'failure' } } }; } return state; };
3. 创建选择器筛选页面请求
用Reselect创建缓存型选择器,避免不必要的重计算:
import { createSelector } from 'reselect'; const selectAllRequests = state => state.requests.requests; // 获取当前页面所有pending请求 export const selectPagePendingRequests = createSelector( [selectAllRequests, (state, page) => page], (requests, page) => { return Object.values(requests).filter( req => req.page === page && req.status === 'pending' ); } ); // 获取当前页面的API列表 export const selectPageEndpoints = createSelector( [selectAllRequests, (state, page) => page], (requests, page) => { return [...new Set( Object.values(requests) .filter(req => req.page === page) .map(req => req.endpoint) )]; } );
4. 在页面组件中控制Spinner
在/home页面中,通过选择器获取pending请求状态,控制Spinner显示:
import { useSelector } from 'react-redux'; import { selectPagePendingRequests } from './selectors'; const HomePage = () => { const pendingRequests = useSelector(state => selectPagePendingRequests(state, 'home')); const isLoading = pendingRequests.length > 0; return ( <div> {isLoading && <div className="spinner">loading...</div>} {/* 页面业务内容 */} </div> ); };
5. 页面卸载时清理状态(可选)
如果需要在页面离开后清理该页面的请求记录,可在组件卸载时dispatch清理action:
import { useEffect } from 'react'; import { useDispatch } from 'react-redux'; const HomePage = () => { const dispatch = useDispatch(); useEffect(() => { return () => { dispatch({ type: 'CLEAR_PAGE_REQUESTS', payload: 'home' }); }; }, [dispatch]); // ...其余逻辑 }; // 在requestReducer中添加清理逻辑 case 'CLEAR_PAGE_REQUESTS': const filteredRequests = Object.fromEntries( Object.entries(state.requests).filter( ([_, req]) => req.page !== action.payload ) ); return { ...state, requests: filteredRequests };
简化方案:用Redux Toolkit快速实现
如果使用Redux Toolkit,可借助createAsyncThunk和createSlice简化状态管理:
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; // 定义带页面元信息的异步action export const fetchUserNames = createAsyncThunk( 'home/fetchUserNames', async () => apiCall('/userNames'), { meta: { page: 'home', endpoint: '/userNames' } } ); // 创建请求状态slice const requestSlice = createSlice({ name: 'requests', initialState: {}, extraReducers: (builder) => { builder // 匹配所有pending请求 .addMatcher( action => action.type.endsWith('/pending'), (state, action) => { const key = `${action.meta.page}-${action.meta.endpoint}`; state[key] = { status: 'pending', ...action.meta }; } ) // 匹配所有成功/失败请求 .addMatcher( action => action.type.endsWith('/fulfilled') || action.type.endsWith('/rejected'), (state, action) => { const key = `${action.meta.page}-${action.meta.endpoint}`; state[key].status = action.type.endsWith('/fulfilled') ? 'success' : 'failure'; } ); } });
后续同样使用选择器筛选页面请求即可。
内容的提问来源于stack exchange,提问作者Aniket Deshpande
相关产品推荐
相关产品推荐

