第一章:Redux 基础概念
1.1 什么是 Redux
| 概念名称 | 说明 | 注意事项 |
|---|
Redux | 状态管理库,集中管理 JavaScript 应用的状态,适用于大型、状态频繁变化的应用。例如在单页应用中管理用户登录状态、购物车数据等 | 不是每个项目都需要 Redux,小型应用可直接使用组件内状态 |
1.2 Redux 核心原则
| 概念名称 | 说明 | 注意事项 |
|---|
| 单一数据源 | 整个应用的 state 被存储在单一的 store 中,便于调试、持久化和状态追踪,所有组件共享一个 store 对象 | 避免创建多个 store,保持状态集中 |
| 状态只读 | 不能直接修改 state,只能通过 dispatch action 来触发变更,保证状态变化可预测。例如不能写 state.count++,必须 dispatch({ type: 'INCREMENT' }) | 直接修改 state 不会触发视图更新,且违反 Redux 原则 |
| 使用纯函数更新 | 使用 reducer 函数根据旧 state 和 action 计算出新 state,确保状态变化可追踪、可测试,即 reducer(state, action) => newState | reducer 必须是纯函数,不能有副作用(如 API 调用、修改参数) |
1.3 Store、State、Action、Reducer 概念解析
| 概念名称 | 说明 | 注意事项 |
|---|
Store | 通过 createStore(reducer) 创建,存储整个应用的状态树,提供 getState、dispatch、subscribe 接口。例如 const store = createStore(counterReducer) | 应用中只应有一个 store |
State | store.getState() 的返回值,表示应用在某一时刻的状态快照。例如 { count: 0, user: null } | 状态是只读的,不能直接修改 |
Action | 普通 JavaScript 对象,必须有 type 字段,描述状态变化的意图(“做什么”)。例如 { type: 'INCREMENT', payload: 1 } | action 必须是对象,type 字段不可省略,建议使用常量定义 type |
Reducer | (state, action) => newState,根据 action 的 type 决定如何更新 state。例如 function counterReducer(state = 0, action) { ... } | 必须是纯函数;不能修改 state,应返回新对象;不能有异步操作 |
第二章:Store 的创建与管理
2.1 创建 Store(createStore)
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
createStore | createStore(reducer, [preloadedState], [enhancer]) | 创建 Redux store 来存放应用的全局状态 | import { createStore } from 'redux';
const store = createStore(counterReducer); | 第二个参数为可选的初始状态,第三个参数用于添加中间件或增强器;reducer 必须是函数 |
2.2 获取状态(getState)
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
getState | store.getState() | 获取当前 store 中保存的 state 树 | const currentState = store.getState();
console.log(currentState); | 返回的是当前状态的引用,但状态不可直接修改;常用于调试或同步读取状态 |
2.3 监听状态变化(subscribe)
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
subscribe | store.subscribe(listener) | 注册一个监听函数,当 state 更新时自动执行 | const unsubscribe = store.subscribe(() => {
console.log('状态更新:', store.getState());
}); | 监听器会在每次 dispatch action 后被调用;不保证每次状态变化都不同,需手动比较前后状态;适用于非 React 环境的视图更新 |
2.4 销毁监听器
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
unsubscribe | 由 subscribe 返回的函数调用 | 移除状态变化的监听器,防止内存泄漏 | const unsubscribe = store.subscribe(() => {
console.log('监听中...');
});
// 停止监听
unsubscribe(); | 一旦调用,该监听器不再执行;在组件卸载或不需要监听时务必调用,避免性能问题 |
第三章:Action 与 Action Creator
3.1 定义 Action 对象
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
| Action 对象 | { type: string, ...payload } | 描述状态变更的意图,是 store 的唯一数据来源 | { type: 'ADD_TODO', payload: '学习 Redux' } | 必须包含 type 字段;type 通常为常量字符串;建议使用 payload 字段传递数据 |
3.2 使用 Action Creator 生成 Action
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
Action Creator | 函数,返回一个 action 对象 | 封装 action 的创建逻辑,便于复用和测试 | function addTodo(text) {
return {
type: 'ADD_TODO',
payload: text
};
}
// 使用:
store.dispatch(addTodo('写代码')); | 函数必须返回一个合法的 action 对象;本身不触发状态变更,需配合 dispatch 使用 |
| 常量定义 type | const ADD_TODO = 'ADD_TODO' | 集中管理 action type,避免拼写错误 | const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
function increment() {
return { type: INCREMENT };
} | 推荐将所有 type 定义为常量并集中存放,提升可维护性 |
3.3 异步 Action 简介
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
| 异步 Action(概念) | 返回函数或使用中间件处理的 action | 处理异步操作,如 API 请求、定时任务 | // 使用 redux-thunk:
function fetchUser() {
return (dispatch) => {
dispatch({ type: 'FETCH_USER_START' });
api.getUser().then(user => {
dispatch({ type: 'FETCH_USER_SUCCESS', payload: user });
});
};
} | 普通 reducer 无法处理异步逻辑;必须引入中间件(如 redux-thunk)才能支持函数形式的 action |
| 中间件支持异步 | applyMiddleware(thunk) | 扩展 dispatch 功能,使其能处理函数或 Promise | const store = createStore(
reducer,
applyMiddleware(thunk)
); | createStore 时需应用中间件,否则 dispatch 函数类型的 action 会报错 |
第四章:Reducer 函数设计
4.1 Reducer 基本结构
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
| Reducer 函数 | (state, action) => newState | 根据当前 state 和接收到的 action 计算并返回新的 state | function counterReducer(state = 0, action) {
switch (action.type) {
default:
return state;
}
} | 必须指定初始状态(如 state = 0);必须有默认返回,防止未知 action 导致 state 为 undefined |
4.2 处理不同 Action 类型
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
switch 语句处理 action.type | switch(action.type) { case 'TYPE': ... } | 根据不同的 action 类型执行不同的状态更新逻辑 | function counterReducer(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
} | 每个 case 应返回新状态;必须包含 default 分支返回原 state;避免在 case 中使用 break,以免遗漏 return 导致错误 |
4.3 使用 combineReducers 合并多个 Reducer
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
combineReducers | combineReducers({ reducer1, reducer2 }) | 将多个子 reducer 合并为一个根 reducer,用于管理复杂状态树 | function todos(state = [], action) { ... }
function filters(state = 'all', action) { ... }
const rootReducer = combineReducers({
todos,
filters
});
const store = createStore(rootReducer); | 参数为一个对象,键名对应 state 的字段名;合并后的 state 结构由对象键决定;每个子 reducer 只管理自己的局部 state |
| 嵌套状态结构 | state.todos / state.filters | 访问 combineReducers 生成的嵌套 state | const todoList = store.getState().todos;
const filter = store.getState().filters; | 使用 combineReducers 后,state 变为分层结构,需通过属性访问对应模块 |
4.4 Reducer 编写注意事项
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
| 纯函数要求 | 不修改参数、无副作用、相同输入始终返回相同输出 | 确保状态更新可预测、可调试 | // 正确:返回新数组
return [...state, newItem];
// 错误:修改原数组
state.push(newItem); return state; | 避免使用 push、pop、splice 等会修改原数组的方法 |
| 状态不可变性(Immutability) | 始终返回新对象/数组,而非修改原对象 | 防止意外状态变更,支持时间旅行调试 | // 数组添加元素
return [...state, action.payload];
// 对象更新属性
return { ...state, name: action.payload }; | 对象和数组都应使用扩展运算符或库(如 immer)创建副本,禁止直接赋值修改 |
| 默认返回原 state | return state; | 处理未知 action 时保持状态不变 | default:
return state; | 必须包含 default 分支,否则未知 action 会导致 state 变为 undefined |
第五章:中间件与异步操作
5.1 中间件概念(Middleware)
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
| Middleware(中间件) | ({ getState, dispatch }) => next => action | 扩展 Redux 的功能,拦截或处理 dispatch 的 action | const logger = store => next => action => {
console.log('dispatching:', action);
const result = next(action);
console.log('next state:', store.getState());
return result;
} | 中间件位于 action 发出之后、到达 reducer 之前;可用于日志、异步处理、路由等;函数结构为三层箭头函数 |
5.2 应用中间件(applyMiddleware)
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
applyMiddleware | applyMiddleware(middleware1, middleware2) | 将多个中间件应用到 createStore 中,增强 store 功能 | import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import logger from './logger';
const store = createStore(
reducer,
applyMiddleware(thunk, logger)
); | 必须作为 createStore 的第二个参数或通过 enhancer 传入;中间件执行顺序从左到右;是使用异步 action 的前提 |
5.3 使用 redux-thunk 实现异步 Action
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
redux-thunk | 函数形式的 action creator | 允许 action creator 返回一个函数,接收 dispatch 和 getState | function fetchPosts() {
return (dispatch, getState) => {
dispatch({ type: 'FETCH_POSTS_START' });
api.getPosts().then(posts => {
dispatch({
type: 'FETCH_POSTS_SUCCESS',
payload: posts
});
});
};
}
// 使用:
store.dispatch(fetchPosts()); | 必须先通过 applyMiddleware(thunk) 启用;返回的函数可访问 dispatch 和 getState,适合处理异步逻辑;不能在普通 reducer 中直接调用 API |
| 异步三态处理 | REQUEST / SUCCESS / FAILURE | 管理异步操作的加载、成功、失败状态 | { type: 'FETCH_USER_REQUEST' }
{ type: 'FETCH_USER_SUCCESS', payload: user }
{ type: 'FETCH_USER_FAILURE', error: 'Network error' } | 建议为每个异步流程定义三种 action 类型,便于 UI 显示加载状态和错误提示 |
5.4 常见中间件介绍
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
redux-logger | import { createLogger } from 'redux-logger';
const logger = createLogger();
applyMiddleware(logger) | 在控制台打印 action 和状态变化,用于调试 | // 控制台输出:
action FETCH_POSTS_START @ 10:20:30
prev state: { posts: [] }
action: { type: 'FETCH_POSTS_START' }
next state: { loading: true } | 建议仅在开发环境使用,生产环境应移除以避免性能损耗 |
redux-saga | 使用 generator 函数管理副作用 | 处理复杂的异步流程、任务调度、并发控制 | import { takeEvery, call, put } from 'redux-saga/effects';
function* fetchUserSaga() {
try {
const user = yield call(api.fetchUser);
yield put({ type: 'FETCH_SUCCESS', payload: user });
} catch (e) {
yield put({ type: 'FETCH_FAIL' });
}
} | 学习成本较高,适用于复杂异步场景;比 thunk 更强大但更重;需单独启动 saga 任务(sagaMiddleware.run) |
注意:普通 reducer 无法处理异步逻辑,必须先通过 applyMiddleware 启用相应中间件(如 redux-thunk 或 redux-saga),否则 dispatch 函数类型的 action 会导致报错。
第六章:Redux 与 React 集成
6.1 使用 react-redux 提供 Provider
| 方法/组件 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
Provider | <Provider store={store}>...</Provider> | 将 Redux store 注入 React 组件树,使所有子组件可通过 useSelector/useDispatch 访问 store | import { Provider } from 'react-redux';
import store from './store';
function App() {
return (
<Provider store={store}>
<CounterDisplay />
</Provider>
);
} | 必须在根组件(如 App)外层包裹;store 只需传入一次;Provider 不渲染 DOM 元素,仅传递上下文 |
6.2 使用 useSelector 读取状态
| 钩子名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
useSelector | const value = useSelector(selectorFn) | 从 Redux store 中提取所需状态 | import { useSelector } from 'react-redux';
function CounterDisplay() {
const count = useSelector(state => state.count);
return <div>{count}</div>;
} | selector 函数接收整个 state 并返回需要的部分;组件会在 selector 返回值变化时重新渲染;避免在 selector 中返回新对象(如 state.todos.map(...) 应 memoize) |
6.3 使用 useDispatch 派发 Action
| 钩子名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
useDispatch | const dispatch = useDispatch() | 获取 dispatch 函数,用于派发 action | import { useDispatch } from 'react-redux';
import { increment } from './actions';
function CounterButton() {
const dispatch = useDispatch();
return (
<button onClick={() => dispatch(increment())}>+1</button>
);
} | useDispatch 返回的是 store.dispatch 的引用;应在事件处理函数中调用 dispatch;避免在渲染过程中直接 dispatch(除非在 useEffect 内) |
6.4 使用 connect 连接组件(旧版 API)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
connect | connect(mapStateToProps, mapDispatchToProps)(Component) | 高阶函数,将 React 组件连接到 Redux store(类组件时代标准) | function Counter({ count, onIncrement }) {
return (
<div>
<span>{count}</span>
<button onClick={onIncrement}>+1</button>
</div>
);
}
const mapStateToProps = state => ({
count: state.count
});
const mapDispatchToProps = {
onIncrement: () => ({ type: 'INCREMENT' })
};
export default connect(mapStateToProps, mapDispatchToProps)(Counter); | mapStateToProps 提供 state 映射,mapDispatchToProps 提供 action 派发方法;已逐渐被 hooks(useSelector/useDispatch)取代;适用于类组件或需深度优化的场景 |
mapStateToProps | (state) => object | 将 store 中的状态映射为组件的 props | const mapStateToProps = state => ({
user: state.user,
loading: state.loading
}); | 必须是纯函数;返回对象将合并到组件 props;若不监听 state,可设为 null |
mapDispatchToProps | object 或 (dispatch) => object | 将 action creator 绑定到 props,或返回 dispatch 调用 | // 方式一:对象形式(自动绑定)
const mapDispatchToProps = {
increment,
decrement
}
// 方式二:函数形式
const mapDispatchToProps = dispatch => ({
onClick: () => dispatch({ type: 'CLICK' })
}); | 对象形式会自动调用 bindActionCreator;函数形式更灵活,适合封装逻辑;不传则默认注入 dispatch |
第七章:项目结构与最佳实践
7.1 典型文件组织方式
| 组织方式 | 结构说明 | 用途 | 代码示例(目录结构) | 注意事项 |
|---|
| 按类型组织(by type) | 将相同类型的文件归类到同一目录 | 适合小型项目或初学者快速上手 | src/
actions/
counter.js
user.js
reducers/
counter.js
user.js
index.js
store.js | 随着功能增多,跨文件跳转频繁,维护成本上升;功能逻辑分散 |
| 按功能组织(by feature) | 每个功能模块包含自己的 action、reducer、state | 提高模块内聚性,便于团队协作和代码复用 | src/
features/
counter/
counterSlice.js
counterActions.js
user/
userSlice.js
userService.js
store.js | 推荐现代 Redux 项目使用;功能独立,易于拆分和测试 |
7.2 Ducks 模式简介
| 概念名称 | 语法/结构 | 用途 | 代码示例 | 注意事项 |
|---|
| Ducks 模式 | 将 action type、action creator 和 reducer 定义在同一个文件中 | 减少文件拆分,提升模块化程度,遵循”一个功能一个文件”原则 | // features/counter/counter.js
const INCREMENT = 'counter/INCREMENT';
const DECREMENT = 'counter/DECREMENT';
export function increment() {
return { type: INCREMENT };
}
export function decrement() {
return { type: DECREMENT };
}
const initialState = 0;
export default function counterReducer(state = initialState, action) {
switch (action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
default:
return state;
}
} | action type 应带模块前缀(如 'counter/INCREMENT')避免冲突;最终仍可通过 combineReducers 合并;适合中等复杂度项目 |
7.3 状态设计原则
| 原则名称 | 说明 | 用途 | 代码示例 | 注意事项 |
|---|
| 状态扁平化 | 尽量使用一维或二维结构,避免深层嵌套 | 提高查找效率,简化更新逻辑 | {
users: {
byId: { 1: { name: 'Alice' } },
allIds: [1]
}
} | 推荐使用”ID 列表 + 查找表”结构管理集合数据 |
| 单一数据源 | 每个数据只在 state 中存在一份 | 避免数据冗余和不一致 | 用户信息只在 user 模块中定义,不重复出现在 order 模块 | 所有组件共享同一份状态,通过 selector 提取 |
| 可预测性 | 状态变化应由 action 明确描述 | 便于调试和回放 | dispatch({ type: 'SET_USER', payload: user }) | 避免在多个 reducer 中响应同一 action 修改不同状态,除非有明确依赖 |
| 不可变更新 | 始终返回新对象,不修改原 state | 保证 Redux 时间旅行和性能优化有效 | return { ...state, name: 'Bob' };
return state.filter(id => id !== 1); | 使用扩展运算符、filter、map 等函数式方法;避免直接 push、splice、赋值修改 |
| 工具名称 | 语法/使用方式 | 用途 | 代码示例 | 注意事项 |
|---|
| Redux DevTools Extension | 浏览器插件(Chrome/Firefox) | 实时查看 action、state 变化,支持时间旅行调试 | 安装插件后自动生效 | 必须在 createStore 时启用增强器才能使用完整功能 |
| DevTools 增强器 | window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() | 在代码中启用 DevTools 功能 | const store = createStore(
rootReducer,
applyMiddleware(thunk),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
); | 生产环境应移除或通过条件判断禁用:process.env.NODE_ENV !== 'production' |
configureStore(RTK) | import { configureStore } from '@reduxjs/toolkit'; | Redux Toolkit 默认集成 DevTools,无需手动添加增强器 | const store = configureStore({ reducer }) | 推荐新项目使用 RTK,开箱即用调试支持 |
| 概念名称 | 说明 | 用途 | 代码示例 | 注意事项 |
|---|
| Redux Toolkit (RTK) | 官方推荐的 Redux 逻辑编写方式 | 解决传统 Redux 样板代码过多、配置复杂的问题,提供高效、简洁的 API | import { createSlice, configureStore } from '@reduxjs/toolkit'; | 是 Redux 官方团队维护的现代工具集;新项目应优先使用 RTK 而非原始 Redux 写法;内置了不可变更新、DevTools 支持等 |
8.2 createSlice 简化 reducer 和 action 创建
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
createSlice | createSlice({ name, initialState, reducers }) | 自动生成 action creator 和 action type,并创建对应的 reducer | import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: 0,
reducers: {
incremented: state => state + 1,
decremented: state => state - 1,
added: (state, action) => state + action.payload
}
});
export const { incremented, decremented, added } = counterSlice.actions;
export default counterSlice.reducer; | reducer 中可”直接修改”state(借助 immer 实现不可变更新);action type 自动生成为 ${name}/${reducerName}(如 'counter/incremented');自动创建 action creator |
| 自动生成 action type | ${name}/${reducerKey} | 命名规范化,避免手动定义字符串常量 | incremented().type // 'counter/incremented' | 无需手动定义 ADD_TODO 等常量,减少出错可能 |
| ”直接修改” state | state++ | 在 reducer 中看似修改原 state,实则生成新对象 | case: incremented:
state.value++ // 合法 | 仅在 createSlice 或 createReducer 中支持;依赖 immer 库,不可用于普通 reducer |
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
configureStore | configureStore({ reducer, middleware?, devTools?, preloadedState }) | 简化 store 创建过程,自动合并 reducer、应用默认中间件并启用 DevTools | import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './features/counter/counterSlice';
const store = configureStore({
reducer: {
counter: counterReducer
}
});
export default store; | 自动调用 combineReducers;默认包含 redux-thunk 和 DevTools 增强器;开发环境自动启用日志和检查;比 createStore 更安全、简洁 |
| 合并多个 slice reducer | { sliceName: slice.reducer } | 构建根 reducer 对象 | const store = configureStore({
reducer: {
counter: counterSlice.reducer,
user: userSlice.reducer
}
}); | 键名决定最终 state 的结构(如 state.counter) |
8.4 异步逻辑处理:createAsyncThunk
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
createAsyncThunk | createAsyncThunk(typePrefix, asyncCallback) | 创建处理异步逻辑的 thunk action,自动 dispatch pending/fulfilled/rejected action | import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
export const fetchUser = createAsyncThunk(
'user/fetchUser',
async (userId, { rejectWithValue }) => {
const response = await api.getUser(userId);
return response.data;
}
);
const userSlice = createSlice({
name: 'user',
initialState: { data: null, loading: false },
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => {
state.loading = true;
})
.addCase(fetchUser.fulfilled, (state, action) => {
state.data = action.payload;
state.loading = false;
})
.addCase(fetchUser.rejected, (state) => {
state.loading = false;
});
}
}); | typePrefix 用于生成 action type(如 user/fetchUser/pending);asyncCallback 接收参数和 thunk API;自动处理异步三态 |
extraReducers | builder => builder.addCase(...) | 在 createSlice 中处理来自 createAsyncThunk 或其他 action 的状态更新 | extraReducers: (builder) => {
builder.addCase(fetchUser.fulfilled, (state, action) => {
state.data = action.payload;
});
} | 用于响应”非本 slice 定义”的 action;推荐使用函数形式(builder callback)以支持类型推断 |
提示:Redux Toolkit 是 Redux 官方推荐的现代写法,新项目应优先使用 RTK 的 configureStore、createSlice 和 createAsyncThunk,它们大幅减少了样板代码并内置了 immer、DevTools 等实用功能。