Redux三大原则体现为:单一数据源对应唯一store,状态只读要求通过dispatch触发变更,纯函数reducer确保相同输入恒得相同输出且无副作用。

Redux 不是必须的,但当你需要在多个组件间共享、追踪和调试状态变更时,它提供了一套可预测的约束机制。核心不是“用 Redux”,而是理解它的三个设计契约:单一数据源、状态只读、变更通过纯函数描述。
Redux 的三大原则怎么体现在代码里?
这三条不是抽象概念,而是直接对应到 createStore、reducer 和 dispatch 的行为约束:
-
单一数据源:整个应用只有一个
store,所有状态都挂在其getState()返回的对象上,不能分散在多个 store 或组件 state 里 -
状态只读:你永远不能直接修改
store.getState()返回的对象(比如state.count++),只能通过dispatch(action)触发变更 -
变更通过纯函数描述:
reducer必须是纯函数——相同输入必得相同输出,且不产生副作用(不能调用API、不能修改入参、不能使用Date.now()等)
为什么 reducer 必须返回新对象而不是修改原 state?
因为 React 的 useState 和 useReducer 都依赖引用变化做浅比较来触发重渲染。如果 reducer 直接改了 state.todos.push(item),引用没变,组件就不会更新——这是最常踩的坑。
正确做法是用展开运算符或 immer:
立即学习“Java免费学习笔记(深入)”;
const todosReducer = (state = [], action) => {
switch (action.type) {
case 'ADD_TODO':
// ✅ 正确:返回新数组
return [...state, { id: Date.now(), text: action.text, done: false }];
case 'TOGGLE_TODO':
// ✅ 正确:映射出新数组,只改目标项
return state.map(todo =>
todo.id === action.id ? { ...todo, done: !todo.done } : todo
);
default:
return state;
}
};
Redux Toolkit(RTK)怎么简化这些规则?
手写 reducer 容易违反纯函数原则或忘记返回新对象。RTK 的 createSlice 在底层自动用 immer 包装,允许你“写起来像修改原对象”,实际仍生成不可变更新:
import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
incremented: state => {
state.value += 1; // ❗看起来是修改,其实是安全的
},
decremented: state => {
state.value -= 1;
}
}
});
export const { incremented, decremented } = counterSlice.actions;
注意:state 参数在这里是 immer 的代理对象,不是原始 state;但你依然不能在 reducer 里写异步逻辑、不能调用 console.log(除非开发环境)、不能解构赋值后直接改字段(如 const s = state; s.value++ 会失效)。
真正难的不是写 reducer,而是决定哪些状态该进 store、哪些该留在组件本地;以及当异步逻辑(如 API 请求)介入时,如何用 createAsyncThunk 把副作用隔离清楚——这部分一旦混进 reducer,整个可预测性就崩了。











