当应用规模膨胀、跨组件共享的状态管理变得错综复杂时,轻量级方案(如 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 是一个函数而非普通对象,函数接收 dispatch 和 getState,可在内部执行异步操作后再 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 生态非常有帮助,即便你最终选择了更轻量的方案。