call(back)

Predict the State: A Redux Reducer

What is the state after the three dispatches? Explain why.

const initialState = { foo: [1], switch: false };

function FooReducer(state = initialState, action) {
  switch (action.type) {
    case "SET_FOO":
      return Object.assign({}, state, { foo: action.foo, switch: true });
    case "PUSH_FOO":
      return Object.assign({}, state, {
        foo: [...state.foo, action.foo],
        switch: !state.switch,
      });
    default:
      return state;
  }
}

dispatch({ type: "SET_FOO", foo: [1, 2] });
dispatch({ type: "PUSH_FOO", foo: 3 });
dispatch({ type: "PUSH_FOO", foo: 6 });

Hints

Hint 1

Trace the reducer one action at a time: each case returns a new object built from the previous state.

Hint 2

Watch switch: the set makes it true, and each push flips it.

Solution

Show the approach

Answer: { foo: [1, 2, 3, 6], switch: true }

SET_FOO replaces foo with [1, 2] and sets switch to true. Each PUSH_FOO appends to a copy of foo and flips switch: false after the first push, true after the second.

Worth saying out loud

  • Read case labels character by character. If a case were written "SET FOO" while the actions use "SET_FOO", every action would fall through to default and the state would stay { foo: [1], switch: false } — with no error anywhere.
  • Object.assign({}, state, ...) and the spread both build a new object, which is what lets connect or useSelector notice the change.
  • Action type constants (or Redux Toolkit's createSlice) exist to make that typo impossible.

Asked at