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

React应用初始化阶段多Redux请求完成/报错前显示全屏加载器的最佳实现方式

这是React项目初始化阶段很典型的加载场景,我给你分享几个实用的实现方案,你可以根据项目规模和现有技术栈来选择:

方案一:用Promise.allSettled直接跟踪所有请求

这个方案最直接,不需要改动太多现有Redux逻辑,适合快速实现:

步骤:

  1. 确保你的异步Action返回Promise:如果用的是Redux Thunk(或者Redux Toolkit的createAsyncThunk),你的pagesRequest、articlesRequest这些Action本身就会返回Promise,这一步基本不用额外处理。
  2. 在index.js中收集并等待所有请求:
    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import { Provider } from 'react-redux';
    import store from './store';
    import App from './App';
    import './index.css';
    
    // 初始化基础请求
    const initialRequests = [
      store.dispatch(pagesRequest()),
      store.dispatch(articlesRequest())
    ];
    
    // 如果有token,追加额外请求
    if (localStorage.ACCESS_TOKEN) {
      initialRequests.push(
        store.dispatch(profileRequest()),
        store.dispatch(notificationsRequest())
      );
    }
    
    // 先渲染全屏加载器
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
      <Provider store={store}>
        <div className="fullscreen-loader">
          <div className="spinner"></div>
        </div>
      </Provider>
    );
    
    // 等待所有请求完成(无论成功失败)
    Promise.allSettled(initialRequests)
      .then(() => {
        // 替换成实际应用内容
        root.render(
          <Provider store={store}>
            <App />
          </Provider>
        );
      });
    
  3. 添加加载器样式:在index.css里加上全屏加载的样式:
    .fullscreen-loader {
      position: fixed;
      top: 0;
      left: 0;
      width: 100vw;
      height: 100vh;
      background-color: rgba(255, 255, 255, 0.9);
      display: flex;
      justify-content: center;
      align-items: center;
      z-index: 9999;
    }
    
    .spinner {
      width: 50px;
      height: 50px;
      border: 5px solid #f3f3f3;
      border-top: 5px solid #3498db;
      border-radius: 50%;
      animation: spin 1s linear infinite;
    }
    
    @keyframes spin {
      0% { transform: rotate(0deg); }
      100% { transform: rotate(360deg); }
    }
    

优缺点:

  • ✅ 实现简单,侵入性低
  • ❌ 没法在应用组件内感知加载状态,适合小型项目或不需要全局状态跟踪的场景

方案二:用Redux全局状态跟踪初始化加载状态

这个方案更适合中大型项目,能在应用任何组件里获取加载状态,扩展性更好:

步骤:

  1. 创建初始化加载状态的Slice:

    // src/slices/initialLoadingSlice.js
    import { createSlice } from '@reduxjs/toolkit';
    
    const initialState = {
      isLoading: true,
      pendingCount: 0
    };
    
    const initialLoadingSlice = createSlice({
      name: 'initialLoading',
      initialState,
      reducers: {
        incrementPending: (state) => {
          state.pendingCount += 1;
        },
        decrementPending: (state) => {
          state.pendingCount -= 1;
          // 所有请求完成后关闭加载
          if (state.pendingCount === 0) {
            state.isLoading = false;
          }
        }
      }
    });
    
    export const { incrementPending, decrementPending } = initialLoadingSlice.actions;
    export default initialLoadingSlice.reducer;
    

    别忘了把这个Slice加入到你的Redux Store中。

  2. 修改异步Action,添加状态跟踪:
    以pagesRequest为例,其他几个Action做同样修改:

    // src/actions/pagesActions.js
    import { createAsyncThunk } from '@reduxjs/toolkit';
    import { incrementPending, decrementPending } from '../slices/initialLoadingSlice';
    
    export const pagesRequest = createAsyncThunk(
      'pages/fetch',
      async (_, { dispatch }) => {
        dispatch(incrementPending());
        try {
          const res = await fetch('/api/pages');
          return await res.json();
        } catch (err) {
          throw err;
        } finally {
          // 无论成功失败都标记请求完成
          dispatch(decrementPending());
        }
      }
    );
    

    如果是手写的Thunk,逻辑类似:在请求开始时dispatchincrementPending,finally块里dispatchdecrementPending。

  3. 在根组件中根据状态渲染加载器:

    // src/App.js
    import { useSelector } from 'react-redux';
    import './App.css';
    
    function App() {
      const isInitialLoading = useSelector(state => state.initialLoading.isLoading);
    
      // 加载中时显示全屏加载器
      if (isInitialLoading) {
        return (
          <div className="fullscreen-loader">
            <div className="spinner"></div>
          </div>
        );
      }
    
      // 加载完成后渲染应用内容
      return (
        <div className="app-container">
          {/* 你的导航栏、路由等内容 */}
          <Navbar />
          <MainRoutes />
        </div>
      );
    }
    
    export default App;
    
  4. 在index.js中正常渲染应用:
    现在不需要在index.js里手动处理Promise了,直接渲染App即可,Redux状态会自动控制加载器的显示:

    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import { Provider } from 'react-redux';
    import store from './store';
    import App from './App';
    import './index.css';
    
    // 发起初始化请求
    const initialRequests = [
      store.dispatch(pagesRequest()),
      store.dispatch(articlesRequest())
    ];
    if (localStorage.ACCESS_TOKEN) {
      initialRequests.push(
        store.dispatch(profileRequest()),
        store.dispatch(notificationsRequest())
      );
    }
    
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
      <Provider store={store}>
        <App />
      </Provider>
    );
    

优缺点:

  • ✅ 全局状态可访问,能在任意组件中根据加载状态做逻辑处理
  • ✅ 扩展性好,后续新增初始化请求只需添加对应的状态跟踪
  • ❌ 需要修改现有Action,增加少量代码量,但维护性更强

额外注意事项:

  • 全屏加载器的样式要保证覆盖整个视口,z-index设得足够高,避免被其他元素遮挡
  • 如果用户刷新页面,加载器会重新显示,符合用户预期
  • 用Promise.allSettled而不是Promise.all,因为all会在某个请求失败时直接reject,而我们需要等待所有请求(无论成败)都完成后再隐藏加载器

内容的提问来源于stack exchange,提问作者Игорь Быстревский

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 09:17:30