# Unidirectional Data Flow and Immutability — Redux Toolkit / Zustand

Source: https://www.skillbyai.com/en/redux-zustand/f-flow

> Actions in, new state out, UI re-renders.

## One direction, new objects

Redux formalises a **unidirectional data flow**: the UI dispatches an **action** (a plain object with a `type` and usually a `payload`), a **reducer** computes the next state from the current state and the action, the store saves it and notifies subscribers, and components re-render with the new values. Reducers must be **pure** and must not mutate their inputs; they return new objects. Immutability lets libraries detect changes with a cheap reference comparison (`prev !== next`) instead of a deep comparison. Zustand follows the same idea: `set` merges a new state object, and selectors compare by reference.

## An immutable update by hand

What Redux Toolkit will later write for you.

```typescript
type Todo = { id: string; text: string; done: boolean };
type State = { todos: Todo[] };

function todosReducer(state: State, action: { type: string; payload: string }): State {
  switch (action.type) {
    case 'todos/toggled':
      return {
        ...state,
        todos: state.todos.map((t) =>
          t.id === action.payload ? { ...t, done: !t.done } : t
        ),
      };
    default:
      return state; // unknown action: return the same reference
  }
}
```

## Never mutate outside Immer

`state.todos.push(x)` is a bug in a hand-written reducer or in a Zustand `set` callback without the immer middleware: the reference does not change, so subscribers may not update.

**Quiz:** Why do Redux and Zustand rely on immutable updates?

- [ ] So the server can validate the state
- [ ] Because JavaScript objects cannot be mutated
- [ ] To make state smaller in memory
- [x] So changes can be detected with a cheap reference comparison

*Answer:* So changes can be detected with a cheap reference comparison. A new reference signals that something changed.
