如何在 Redux 中发送网络请求?

在 Redux 中发送网络请求需要使用异步中间件如 redux-thunk 或 Redux Toolkit 的 createAsyncThunk 来处理异步逻辑,更新应用状态。

数据管理 中等 Redux 网络请求 异步操作

Redux 本身不直接处理异步逻辑(如网络请求),需要通过中间件实现发送请求的功能。以下是核心方法和流程:

  1. 理解需求
    在 Redux 应用中进行网络请求通常是为了从服务器获取数据并更新应用的 state,这个过程必须是异步的。

  2. 核心解决方案:异步中间件
    Redux 遵循严格的同步数据流规则(dispatch action -> reducer -> state)。要处理异步操作,必须使用中间件 (Middleware) 来增强 store 的功能,使其可以 dispatch 函数或 Promise 而非仅仅普通对象。
    • 流行中间件选择
      • redux-thunk:最常用和基础的选择,允许 action creators 返回函数(不仅仅是对象)。
      • redux-saga:使用生成器函数 (generators) 管理复杂异步流和副作用,功能强大,学习曲线稍陡。
      • redux-toolkit's createAsyncThunk:内置在 Redux Toolkit (官方推荐工具库) 中,抽象掉了大部分样板代码。
  3. 使用 redux-thunk 实现(基本方法)
    这是最常见和最基础的操作方式。
    • 步骤
      1. 安装中间件

        npm install redux-thunk
        
      2. 在 store 中应用中间件applyMiddleware(thunk)

        import { createStore, applyMiddleware } from 'redux';
        import thunk from 'redux-thunk';
        import rootReducer from './reducers';
        
        const store = createStore(rootReducer, applyMiddleware(thunk));
        
      3. 创建异步 Action Creator
        • 通常返回一个以 dispatchgetState 为参数的函数(不是普通的 action 对象)。在这个函数内部执行异步操作(fetch, axios等)。
        • 根据异步操作(开始请求、请求成功、请求失败)的不同阶段,使用 dispatch 发出不同的普通 action 对象
        // Action Types (需要事先定义,如 constants.js)
        const FETCH_USER_REQUEST = 'FETCH_USER_REQUEST';
        const FETCH_USER_SUCCESS = 'FETCH_USER_SUCCESS';
        const FETCH_USER_FAILURE = 'FETCH_USER_FAILURE';
        
        // Action Creators (供组件实际dispatch)
        const fetchUserRequest = () => ({ type: FETCH_USER_REQUEST });
        const fetchUserSuccess = (user) => ({ type: FETCH_USER_SUCCESS, payload: user });
        const fetchUserFailure = (error) => ({ type: FETCH_USER_FAILURE, error: error });
        
        // 异步 Action Creator (使用 redux-thunk)
        export const fetchUser = (userId) => {
          return (dispatch, getState) => {
            // (可选) 可以在 action creator 中使用额外的工具方法或 API 模块 (如 axios, fetchSignIn 示例所示)
            dispatch(fetchUserRequest()); // 指示开始请求,可以设置 state.loading = true 或类似状态
        
            // 执行网络请求(使用 fetch 或 axios)
            fetch(`https://api.example.com/users/${userId}`) // API URL
              .then(response => {
                if (!response.ok) { throw Error(response.statusText); }
                return response.json();
              })
              .then(userData => {
                // 请求成功,dispatch SUCCESS action
                dispatch(fetchUserSuccess(userData));
              })
              .catch(error => {
                // 请求失败,dispatch FAILURE action
                dispatch(fetchUserFailure(error.message));
              });
          };
        };
        
      4. 在 React 组件中 Dispatch 动作
        • 使用 react-reduxconnect(mapDispatchToProps 的方式)在 Class 组件中使用。
        • 或者在函数组件中使用 useDispatch Hook:
        import React, { useEffect } from 'react';
        import { useDispatch } from 'react-redux';
        import { fetchUser } from './actions'; // 导入上一步创建的异步 action creator
        
        const UserComponent = ({ userId }) => {
          const dispatch = useDispatch();
        
          useEffect(() => {
            // 组件挂载或 userId 改变时 dispatch 异步 action
            dispatch(fetchUser(userId)); // <-- dispatch FetchUser 操作
          }, [dispatch, userId]);
        
          // ...渲染组件, 从 state 读取数据(需提前 mapStateToProps 或 useSelector)
        };
        
      5. Reducer 处理同步 Action: 修改相应的 slice state 来处理如 FETCH_USER_SUCCESS, FETCH_USER_FAILURE 等。
        • 初始化状态通常包含如 isLoading, data, error 字段。
        • 根据不同类型的 action 更新状态。
  4. 使用 Redux Toolkit (RTK) - createAsyncThunk 实现(推荐方法)
    Redux Toolkit (RTK) 提供 createAsyncThunk 函数生成异步 action creators,极大简化请求流程并管理常见的 loading, succcess, error 状态。
    • 步骤
      1. 安装和创建 Slice(如果还未使用 RTK)

        npm install @reduxjs/toolkit react-redux
        

        在切片 Slice 文件中:

      2. 使用 createAsyncThunk 创建异步 thunk

        import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
        import axios from 'axios'; // or your API util
        
        // 1. 创建异步 thunk
        export const fetchUser = createAsyncThunk('user/fetchUserStatus', async (userId, thunkAPI) => {
          try {
            const response = await axios.get(`https://api.example.com/users/${userId}`); // <-- API Call 在 createAsyncThunk 函数的函数体中执行
            return response.data; // payload for the fulfilled action
          } catch (err) {
            return thunkAPI.rejectWithValue(err.response.data); // payload for the rejected action
          }
        });
        
      3. 在 Slice 中添加 ExtraReducers 处理异步 pending/fulfilled/rejected 状态: createAsyncThunk 会自动生成三种 Action Types(如 'user/fetchUserStatus/pending')。extraReducers 中监听并自动更新:

        const userSlice = createSlice({
          name: 'user',
          initialState: {
            data: null,
            isLoading: false,
            error: null
          },
          reducers: { /* 如果有同步action,这里写 */ },
          extraReducers: (builder) => { // <- 在 slice 中使用 extraReducers 构建器处理异步操作的状态变更
            builder
              .addCase(fetchUser.pending, (state) => {
                state.isLoading = true;
                state.error = null; // 重置错误状态
              })
              .addCase(fetchUser.fulfilled, (state, action) => {
                state.isLoading = false;
                state.data = action.payload; // 更新数据
              })
              .addCase(fetchUser.rejected, (state, action) => {
                state.isLoading = false;
                state.error = action.error.message || 'An error occurred'; // 设置错误状态
              });
          }
        });
        
      4. 在组件中使用方式与 thunk 类似: 也是调用 dispatch(fetchUser(userId))
      5. 优点:内置处理常见的请求 loading / success / error 生命周期状态转换模式代码较少,符合现代 Redux 开发实践。
  5. 请求管理与状态命名规则
    • 按照异步处理生命周期设计不同的 action(通常命名为类似 Resource/FETCH_REQUEST, Resource/FETCH_SUCCESS, Resource/FETCH_FAILURE
    • State 中可以设置类似 data: {}, isLoading: false, error: null 的通用结构来跟踪处理状态和结果。
    • 使用工具封装可重复使用的请求逻辑。
    • 支持在 ActionCreator (使用 redux thunk) 的方法中使用服务或API模块来处理请求具体细节:
        fetchSignIn(email, password).then(response => ...)
    
    • 支持通过 redux-thunk / createAsyncThunk 函数的额外参数或返回值(rejectWithValue)获取服务响应的数据来更新状态。

总结关键点:Redux 中发送网络请求的核心是通过异步中间件如 redux-thunk 或者 Redux Toolkit 提供的 createAsyncThunk。组件中 dispatch 相关的异步操作 Action Creator;该操作内部执行 API 调用;然后根据响应结果再次正常 dispatch 出实际的“成功”、“失败”同步 action 更新 reducer 数据。管理异步任务必须依赖于支持派发函数的中间件增强 store。使用最新的 Redux Toolkit 极大简化了这一过程。