人人都会AI编程

13.4 大型项目状态分层与模块化设计最佳实践

更新时间:2026-07-10

当应用规模膨胀、跨组件共享的状态管理变得错综复杂时,轻量级方案(如 Context + useReducer)可能难以满足需求。此时,Redux 作为 React 生态中最成熟的重量级状态管理库,凭借严格的数据流控制和丰富的中间件生态,成为大型项目的首选。本节按 Redux 核心概念 → Redux Toolkit 现代写法 → RTK Query 数据请求 → 中间件原理的顺序展开。


13.4.1 Redux 核心概念:Store / Action / Reducer / 中间件

Redux 的核心思想是单一数据源状态只读通过纯函数修改。整个应用的状态存储在一棵 JavaScript 对象树中(Store),唯一改变状态的方式是派发(dispatch)一个 Action,由 Reducer 根据旧状态和 Action 计算出新状态。

Store(仓库)

import { createStore } from 'redux';

const store = createStore(reducer);

Store 负责:

  • 维护整个应用的状态
  • 提供 getState() 读取状态
  • 提供 dispatch(action) 触发更新
  • 通过 subscribe(listener) 注册监听器

Action(动作)
一个普通的 JavaScript 对象,描述“发生了什么”,必须包含 type 字段:

{ type: 'ADD_TODO', payload: '学习 Redux' }

通常使用 Action Creator 函数生成 Action:

const addTodo = (text) => ({ type: 'ADD_TODO', payload: text });

Reducer(处理器)
一个纯函数,接收当前状态和 Action,返回新状态。绝对不能直接修改原状态,必须返回一个新的对象/数组。

const initialState = { todos: [] };

function todoReducer(state = initialState, action) {
  switch (action.type) {
    case 'ADD_TODO':
      return { ...state, todos: [...state.todos, action.payload] };
    case 'REMOVE_TODO':
      return { ...state, todos: state.todos.filter(t => t !== action.payload) };
    default:
      return state;
  }
}

当应用规模变大时,可以将 Reducer 拆分成多个,再用 combineReducers 合并。

数据流

View → dispatch(action) → Reducer → (new)Store → View 重新渲染

严格单向流动,过程中可能经过中间件。


13.4.2 Redux Toolkit(RTK):现代 Redux 标准写法

传统 Redux 的手写样板代码、不可变更新逻辑和配置中间件等步骤极为繁琐。Redux Toolkit(RTK) 是 Redux 官方推荐的标准工具集,它内置了 Immer(允许“可变”地修改状态,内部自动转成不可变更新)、Redux Thunk、以及简化的 API。

创建 Slice
一个 Slice 将 Reducer 和 Action Creator 统一封装,不再需要单独定义 Action 类型和 Action Creator。

import { createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    increment(state) {
      state.value += 1; // 看似直接修改,Immer 会生成新对象
    },
    decrement(state) {
      state.value -= 1;
    },
    incrementByAmount(state, action) {
      state.value += action.payload;
    },
  },
});

export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;

配置 Store

import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
});

configureStore 自动启用了 Redux DevTools、Thunk 中间件和开发环境的检查。

在 React 中使用

import { useSelector, useDispatch } from 'react-redux';
import { increment } from './counterSlice';

function Counter() {
  const count = useSelector(state => state.counter.value);
  const dispatch = useDispatch();

  return (
    <button onClick={() => dispatch(increment())}>
      Count: {count}
    </button>
  );
}

RTK 是官方推荐的所有新 Redux 项目的起点,写得少、清晰、安全。


13.4.3 RTK Query:数据请求与缓存能力

在 Redux 应用中处理服务端状态一直是个痛点:需要手动编写 thunk、管理加载状态、缓存和失效逻辑。RTK Query 被内置于 Redux Toolkit 中,专门解决数据获取和缓存问题,理念类似 TanStack Query,但紧密集成 Redux。

RTK Query 通过 createApi 定义 API 端点,自动生成带有缓存和状态跟踪的 hooks。

定义 API 服务

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const api = createApi({
  reducerPath: 'api',
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
  endpoints: (builder) => ({
    getPosts: builder.query({
      query: () => 'posts',
    }),
    addPost: builder.mutation({
      query: (newPost) => ({
        url: 'posts',
        method: 'POST',
        body: newPost,
      }),
    }),
  }),
});

export const { useGetPostsQuery, useAddPostMutation } = api;

在组件中使用

function PostsList() {
  const { data, error, isLoading } = useGetPostsQuery();
  
  if (isLoading) return <div>加载中...</div>;
  if (error) return <div>出错了</div>;

  return (
    <ul>
      {data.map(post => <li key={post.id}>{post.title}</li>)}
    </ul>
  );
}

RTK Query 自动处理:

  • 数据缓存,避免重复请求
  • 乐观更新、标签失效系统
  • 请求去重、重新聚焦时轮询
  • 与 Redux DevTools 集成,方便调试

如果你的项目已经使用 Redux,RTK Query 是管理服务端状态的最自然选择,减少了需要从 Redux 中存储的“服务器状态”样板代码。


13.4.4 Redux 中间件原理与常用中间件

中间件原理
Redux 中间件是在 Action 到达 Reducer 之前执行的一段链条逻辑。中间件的本质是一个嵌套函数,它能访问 Store 实例、下一个中间件和最终 dispatch。其函数签名通常为:

const middleware = store => next => action => {
  // 可在此处处理 action 前/后
  return next(action);
};

next 是调用下一个中间件或原始 dispatch 的方法。中间件可以用于日志记录、崩溃报告、异步操作等。

常用中间件

  • Redux Thunk(内置在 RTK)

允许 action 是一个函数而非普通对象,函数接收 dispatchgetState,可在内部执行异步操作后再 dispatch 普通 action。

  const fetchUser = (id) => async (dispatch) => {
    dispatch({ type: 'LOADING_START' });
    const response = await fetch(`/api/user/${id}`);
    const user = await response.json();
    dispatch({ type: 'LOAD_USER', payload: user });
  };
  
  • Redux Saga(独立库)

使用 Generator 函数处理副作用,以声明方式控制异步流程,适合复杂异步场景(如竞态、并行、取消请求等),但学习曲线较陡。如今大多数场景 Thunk 已够用,Saga 使用率在下降。

  • Redux Logger

开发环境中在控制台打印每个 Action 和状态变化,便于调试。

  • Redux Persist

将 Redux 状态持久化到 localStorage,页面刷新后状态不丢失。

选择建议

  • 绝大多数项目,使用 Redux Toolkit 内置的 Thunk 处理异步即可。
  • 数据请求强烈建议使用 RTK Query,而不是手写 Thunk。
  • 如果需要复杂的副作用编排,再考虑 Saga 或观察流(如 Redux-Observable)。

13.4.5 Redux 的适用场景与总结

适用场景

  • 大型团队开发的中大型应用,需要严格规范的状态管理。
  • 应用状态频繁共享于多个不相关组件,并且状态更新逻辑复杂。
  • 需要可预测的状态变化,支持时间旅行调试的需求。

不适用场景

  • 小型项目或状态仅仅局部于少数组件:使用 Zustand、Jotai 甚至 React Context 更轻量。
  • 服务器状态远多于客户端状态:优先考虑 TanStack Query,而不是强行放 Redux。

总结
Redux 是一套严格但强大的数据流管理方案。现代 Redux = RTK + RTK Query + Hooks,上手成本已大大降低,仍是许多大型企业应用的首选。理解它的核心概念(Store、Action、Reducer、中间件)对于深度掌握 React 生态非常有帮助,即便你最终选择了更轻量的方案。