You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

React应用如何实现全局单Loader Reducer(无需单独编写动作)

实现全局加载器的Redux解决方案(无需单独编写加载器Action)

这个需求非常常见,结合你已有的Redux+redux-pack技术栈,我们可以直接利用redux-pack处理Promise时自动生成的生命周期元数据,来实现一个全局单Reducer管理加载状态,完全不需要额外编写加载器相关的Action。

核心思路

redux-pack在处理带promise字段的Action时,会自动分发包含meta.status的Action,状态包括START(请求开始)、SUCCESS/FAILURE/COMPLETE(请求结束)。我们只需要写一个全局Reducer,监听这些带有redux-pack标记的Action,根据状态更新加载计数即可。


步骤1:编写全局Loading Reducer

这个Reducer会跟踪当前活跃的API请求数量,只要有请求在进行,就显示加载器;所有请求完成后自动隐藏。

import { START, SUCCESS, FAILURE, COMPLETE } from 'redux-pack';

const initialState = {
  activeRequests: 0, // 跟踪活跃请求数
};

const loadingReducer = (state = initialState, action) => {
  // 只处理redux-pack管理的Action(带有meta.promise标记)
  if (!action.meta?.promise) {
    return state;
  }

  switch (action.meta.status) {
    case START:
      // 请求开始,活跃数+1
      return {
        ...state,
        activeRequests: state.activeRequests + 1,
      };
    case SUCCESS:
    case FAILURE:
    case COMPLETE:
      // 请求结束,活跃数-1(确保不会变成负数)
      return {
        ...state,
        activeRequests: Math.max(state.activeRequests - 1, 0),
      };
    default:
      return state;
  }
};

export default loadingReducer;

如果需要跟踪单个请求的加载状态(比如某个按钮点击后的加载),可以修改Reducer为按Action Type存储状态:

import { START, SUCCESS, FAILURE, COMPLETE } from 'redux-pack';

const initialState = {
  byActionType: {}, // 按Action Type存储每个请求的加载状态
};

const loadingReducer = (state = initialState, action) => {
  if (!action.meta?.promise) return state;

  const actionType = action.type;
  switch (action.meta.status) {
    case START:
      return {
        ...state,
        byActionType: { ...state.byActionType, [actionType]: true },
      };
    case SUCCESS:
    case FAILURE:
    case COMPLETE:
      return {
        ...state,
        byActionType: { ...state.byActionType, [actionType]: false },
      };
    default:
      return state;
  }
};

步骤2:合并到Root Reducer

将这个全局Loading Reducer合并到你的根Reducer中:

import { combineReducers } from 'redux';
import loadingReducer from './loadingReducer';
// 导入你的其他业务Reducer
import userReducer from './userReducer';
import postReducer from './postReducer';

const rootReducer = combineReducers({
  loading: loadingReducer,
  user: userReducer,
  post: postReducer,
  // ...其他业务Reducer
});

export default rootReducer;

步骤3:实现全局加载器组件

创建一个全局加载器组件,通过Redux获取加载状态并渲染:

import React from 'react';
import { useSelector } from 'react-redux';

const GlobalLoader = () => {
  // 判断是否有活跃请求
  const isLoading = useSelector(state => state.loading.activeRequests > 0);

  if (!isLoading) return null;

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

export default GlobalLoader;

然后在你的App根组件中引入它:

import React from 'react';
import GlobalLoader from './GlobalLoader';
// 其他路由/组件

function App() {
  return (
    <div className="App">
      <GlobalLoader />
      {/* 你的应用内容:导航、路由等 */}
    </div>
  );
}

export default App;

步骤4:确保你的API Action正确使用redux-pack

只要你的业务Action是按照redux-pack的规范编写的(包含promise字段),这个方案就会自动生效。示例Action:

import { FETCH_USER } from './actionTypes';
import api from '../api';

// redux-pack会自动处理这个promise,分发生命周期Action
export const fetchUser = (userId) => ({
  type: FETCH_USER,
  promise: api.getUser(userId),
});

内容的提问来源于stack exchange,提问作者Abin Thaha Azees

火山引擎 最新活动