Redux - Reducer
Redux - 使用 Redux Toolkit 的 Reducer
Section titled “Redux - 使用 Redux Toolkit 的 Reducer”Reducer 是 Redux 中的一个基本概念。它们是纯函数,负责指定应用程序的 state 如何响应 actions 而变化。给定先前的 state 和一个 action,reducer 会计算并返回下一个 state。因为它们是纯函数 (没有副作用,相同的输入总是产生相同的输出),所以它们使得 state 的变化是可预测的。
Reducer 的关键原则:
- 它们绝不能修改其参数 (state 对象或 action)。
- 它们绝不能执行副作用,例如 API 调用或路由跳转。
- 它们绝不能调用非纯函数 (例如
Date.now()或Math.random())。
reducer 的基本签名仍然是 (previousState, action) => newState。
Redux Toolkit 的 createSlice 函数显著简化了 reducer 的编写:
- 它会自动生成 action creator 和 action type。
- 它内部使用 Immer 库,允许你编写看起来像直接修改 state 的代码,但确保底层实现不可变更新。这使得 reducer 逻辑更清晰、更不易出错。
- 它鼓励按功能或 state 的“切片”来组织 reducer。
让我们回顾一下管理项目列表的示例,这些项目可能是从 API 获取的。下面是使用 createSlice 为此定义 slice 的方式:
// features/items/itemsSlice.js
import { createSlice } from '@reduxjs/toolkit';
// 为此 slice 定义初始 stateconst initialState = { list: [], status: 'idle', // 'idle' | 'loading' | 'succeeded' | 'failed' error: null,};
const itemsSlice = createSlice({ name: 'items', initialState, // 为同步 actions 定义 reducer reducers: { // 示例:添加单个项目的 action (payload 是该项目) itemAdded(state, action) { // Immer 允许在这里直接进行“修改” state.list.push(action.payload); }, // 示例:清除所有项目的 action itemsCleared(state) { state.list = []; state.status = 'idle'; state.error = null; } // 我们将在下方添加异步 actions (例如获取数据) 的 reducer }, // 为在其他地方定义的 actions 添加 extraReducers,例如异步 thunk // 通常用于处理异步操作的 pending/fulfilled/rejected 状态 extraReducers(builder) { // 示例:如果使用 createAsyncThunk 来获取项目 /* builder .addCase(fetchItems.pending, (state, action) => { state.status = 'loading'; }) .addCase(fetchItems.fulfilled, (state, action) => { state.status = 'succeeded'; // 将获取到的项目添加到数组中 state.list = state.list.concat(action.payload); }) .addCase(fetchItems.rejected, (state, action) => { state.status = 'failed'; state.error = action.error.message; }); */ }});
// 导出生成的 action creatorsexport const { itemAdded, itemsCleared } = itemsSlice.actions;
// 导出 reducer 函数本身export default itemsSlice.reducer;注意 createSlice 内的 Immer 集成如何允许更简单的语法,例如 state.list.push(action.payload),而无需手动使用扩展语法 (...state) 或 Object.assign/concat 来实现不可变更新。reducers 对象将 action type 名称直接映射到 case reducer 函数。
对于更大的应用程序,你会创建多个 slice,每个 slice 管理应用程序 state 的各自部分。Redux Toolkit 的 configureStore 函数会负责将这些 slice reducer 组合成一个根 reducer。
想象另一个用于用户认证的 slice:
// features/auth/authSlice.js
import { createSlice } from '@reduxjs/toolkit';
const initialState = { user: null, token: null,};
const authSlice = createSlice({ name: 'auth', initialState, reducers: { userLoggedIn(state, action) { state.user = action.payload.user; state.token = action.payload.token; }, userLoggedOut(state) { state.user = null; state.token = null; }, },});
export const { userLoggedIn, userLoggedOut } = authSlice.actions;export default authSlice.reducer;设置 store 时,你会向 configureStore 传递一个包含你的 slice reducer 的对象。它会在内部自动调用 combineReducers。
import { configureStore } from '@reduxjs/toolkit';import itemsReducer from '../features/items/itemsSlice';import authReducer from '../features/auth/authSlice';
export const store = configureStore({ // 这里的 key 决定了 state 对象的结构 reducer: { items: itemsReducer, auth: authReducer, // 在这里添加其他 slice reducer },});
// 最终的 state 结构将是:{ items: { ... }, auth: { ... } }这种方法促进了模块化,并使管理 state 逻辑变得更容易,尤其随着应用程序的增长,同时也将 Redux 的核心原则——纯函数和可预测的 state 变化——置于中心地位。