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

React中如何跟踪多API调用并实现全局加载屏?

全局加载屏实现方案

核心思路是跟踪所有活跃的请求状态,只要有一个请求在加载,就显示全局加载屏。结合useContext和useReducer是最优方案:useContext负责全局状态共享,useReducer负责处理加载状态的添加/移除逻辑,比单独用useState更易维护复杂状态。

步骤1:创建全局加载状态的Context和Reducer

先封装全局状态的管理逻辑,用Set存储当前正在加载的请求标识(确保唯一):

// LoadingContext.js
import { createContext, useContext, useReducer } from 'react';

const LoadingContext = createContext();

// 处理加载状态的更新逻辑
const loadingReducer = (state, action) => {
  switch (action.type) {
    case 'ADD_LOADING':
      return new Set([...state, action.payload]);
    case 'REMOVE_LOADING':
      const updatedState = new Set(state);
      updatedState.delete(action.payload);
      return updatedState;
    default:
      return state;
  }
};

export const LoadingProvider = ({ children }) => {
  const [loadingRequests, dispatch] = useReducer(loadingReducer, new Set());
  // 只要有活跃请求,isLoading就为true
  const isLoading = loadingRequests.size > 0;

  return (
    <LoadingContext.Provider value={{ dispatch, isLoading }}>
      {children}
    </LoadingContext.Provider>
  );
};

// 自定义Hook,简化Context调用
export const useLoading = () => useContext(LoadingContext);

步骤2:封装带全局状态的useAxios Hook

在你原有的useAxios基础上,包装一层,自动同步请求的loading状态到全局:

// useAxiosWithLoading.js
import { useEffect } from 'react';
import { useAxios } from './useAxios'; // 引入你的原useAxios Hook
import { useLoading } from './LoadingContext';

export const useAxiosWithLoading = (requestKey) => {
  const { dispatch } = useLoading();
  const { data, error, loading, ...rest } = useAxios();

  // 监听组件自身的loading状态,更新全局加载状态
  useEffect(() => {
    if (loading) {
      dispatch({ type: 'ADD_LOADING', payload: requestKey });
    } else {
      dispatch({ type: 'REMOVE_LOADING', payload: requestKey });
    }

    // 组件卸载时清理状态,避免内存泄漏
    return () => {
      dispatch({ type: 'REMOVE_LOADING', payload: requestKey });
    };
  }, [loading, dispatch, requestKey]);

  return { data, error, loading, ...rest };
};

requestKey需要是唯一标识,比如'componentA-user-list',避免不同请求的状态互相干扰。

步骤3:在组件中使用封装后的Hook

替换原有useAxios为useAxiosWithLoading,传入唯一的请求标识:

// ComponentA.js
import { useAxiosWithLoading } from './useAxiosWithLoading';

const ComponentA = () => {
  const { data, error, loading } = useAxiosWithLoading('componentA-user-list');

  if (loading) return <div>组件加载中...</div>;
  if (error) return <div>请求失败:{error.message}</div>;

  return (
    <div>
      {data?.map(user => <div key={user.id}>{user.name}</div>)}
    </div>
  );
};

ComponentB、ComponentC同理,只需传入不同的requestKey即可。

步骤4:实现全局加载屏组件

监听全局isLoading状态,状态为true时显示全屏加载遮罩:

// GlobalLoading.js
import { useLoading } from './LoadingContext';

const GlobalLoading = () => {
  const { isLoading } = useLoading();

  if (!isLoading) return null;

  return (
    <div style={{
      position: 'fixed',
      inset: 0,
      backgroundColor: 'rgba(0, 0, 0, 0.5)',
      display: 'flex',
      justifyContent: 'center',
      alignItems: 'center',
      zIndex: 9999,
      color: 'white',
      fontSize: '1.2rem'
    }}>
      全局加载中...
    </div>
  );
};

步骤5:在App根组件中配置Provider和加载屏

把整个应用包裹在LoadingProvider中,并挂载全局加载屏:

// App.js
import { LoadingProvider } from './LoadingContext';
import GlobalLoading from './GlobalLoading';
import ComponentA from './ComponentA';
import ComponentB from './ComponentB';
import ComponentC from './ComponentC';

function App() {
  return (
    <LoadingProvider>
      <GlobalLoading />
      <div className="App">
        <ComponentA />
        <ComponentB />
        <ComponentC />
      </div>
    </LoadingProvider>
  );
}

export default App;

为什么选择useContext+useReducer?

  • useContext:让所有组件能轻松访问全局加载状态,无需逐层传递props。
  • useReducer:把加载状态的更新逻辑集中管理,比useState更适合处理“添加/移除”这类多步状态操作,后期扩展(比如添加请求超时、错误统计)更方便。

如果你的需求非常简单(比如只有3个固定请求),用useState+useContext也能实现,但useReducer的可维护性更强。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 01:05:53