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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 06:42:52