DrillLab
第 83 / 105 道83 / 105 · #350

Redux 的结构和工作流

Redux structure and workflow

先自己答,再往下看Answer it yourself first

一句话:单向环—— view 派发 action → 中间件处理 → reducer 算出新 state → store 更新 → 订阅的组件重渲染。

五个角色:

  • store——唯一的状态容器, 提供 getState /dispatch /subscribe
  • action——描述「发生了什么」的普通对象,必须有 type它只描述,不做事。
  • reducer——(state, action) => newState必须是纯函数
  • middleware—— 在 action 到 reducer 之前拦一道(#354)。
  • selector—— 从 store 里挑出组件需要的那部分。

Redux Toolkit(RTK)改变了什么—— 必答,因为现在没人手写 Redux 了:

  • createSlice一次生成 reducer + action creators + action types, 样板代码少一大半。
  • 内置 Immer, 所以你可以写起来像在改state.list.push(x), 实际产出的是新对象—— 但要注意这只在createSlice 里成立。
  • 默认装好 thunk 和 DevTools。
  • createAsyncThunk管异步的三个状态 (pending / fulfilled / rejected)。

会追问:「为什么必须单向?」—— 因为状态变化的路径唯一, 所以出 bug 时可以从 action 记录里 倒推每一步。 双向绑定的框架里 「这个值到底是谁改的」经常查不清。

In one line: a one-way loop — the view dispatches an action, middleware handles it, a reducer computes the new state, the store updates, and the subscribed components re-render.

Five roles:

  • store — the single container for state, exposing getState, dispatch and subscribe.
  • action — a plain object that describes what happened and must have a type. It only describes; it does nothing.
  • reducer (state, action) => newState, and it has to be pure.
  • middleware — intercepts the action on its way to the reducer (#354).
  • selector — picks the part of the store a component needs.

What Redux Toolkit (RTK) changed — you have to cover this, because nobody hand-writes Redux any more:

  • createSlice generates the reducer, the action creators and the action types at once, cutting more than half the boilerplate.
  • Immer is built in, so you can write what looks like a mutation — state.list.push(x) — and still get a new object out. Just remember this only holds inside createSlice.
  • thunk and DevTools are wired up by default.
  • createAsyncThunk handles the three async states (pending / fulfilled / rejected).

Follow-up: “Why does it have to be one-way?” — because there is exactly one path a change can take, so when something breaks you can walk back through the action log step by step. In a two-way binding framework, “who actually changed this value” is often unanswerable.

JavaScript现在真正会写的 Redux示意Illustrative
1// RTK 的 slice:reducer + actions 一次生成
2const todos = createSlice({
3 name: "todos",
4 initialState: [],
5 reducers: {
6 add(state, action) {
7 state.push(action.payload); // 看着是 mutate,Immer 会产出新 state
8 },
9 toggle(state, action) {
10 const t = state.find((x) => x.id === action.payload);
11 if (t) t.done = !t.done;
12 },
13 },
14});
15
16export const { add, toggle } = todos.actions;
17
18// 组件里
19const list = useSelector((s) => s.todos); // 只订阅这一部分
20const dispatch = useDispatch();
21dispatch(add({ id: Date.now(), text, done: false }));
1// An RTK slice generates the reducer and the actions together
2const todos = createSlice({
3 name: "todos",
4 initialState: [],
5 reducers: {
6 add(state, action) {
7 state.push(action.payload); // it looks like a mutation; Immer produces a new state
8 },
9 toggle(state, action) {
10 const t = state.find((x) => x.id === action.payload);
11 if (t) t.done = !t.done;
12 },
13 },
14});
15
16export const { add, toggle } = todos.actions;
17
18// In the component
19const list = useSelector((s) => s.todos); // subscribes to this part only
20const dispatch = useDispatch();
21dispatch(add({ id: Date.now(), text, done: false }));