Skip to content

Redux - 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 定义初始 state
const 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 creators
export 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。

app/store.js
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 变化——置于中心地位。