๐Ÿง  Understanding useReducer + TypeScript + Generics (Deep Guide for Junior โ†’ Senior Developers)

1. Core Idea: What is useReducer?

useReducer is a state management pattern based on pure functions and actions.

๐Ÿ“ฎ dispatch (messenger)
   โ†“ sends a letter (action)
๐Ÿ‘จโ€๐Ÿ’ผ reducer (manager)
   โ†“ decides what happens
๐Ÿงพ state (company status)

2. What is an Action? (The meaning of payload)

An action is a message describing what happened, plus optional data about it (the payload):

{
  type: "FETCH_SUCCESS",
  payload: products
}
  • type โ†’ the event name (what happened)
  • payload โ†’ the data carried with the event

๐Ÿ“ฆ Think of the payload as the cargo inside a delivery box. It is not logic โ€” it is data transport.

3. State (Generic Structure)

type State<T> = {
  data: T | null;
  loading: boolean;
  error: string | null;
};

T is an unknown data type (Product, User, Categoryโ€ฆ), so this state works for any API.

4. Action (Discriminated Union)

type Action<T> =
  | { type: "FETCH_START" }
  | { type: "FETCH_SUCCESS"; payload: T }
  | { type: "FETCH_ERROR"; payload: string };

Why a union type? Because the manager only understands three valid messages.

5. Reducer (Decision Engine)

function reducer<T>(state: State<T>, action: Action<T>): State<T> {
  switch (action.type) {
    case "FETCH_START":
      return { ...state, loading: true, error: null };
    case "FETCH_SUCCESS":
      return { data: action.payload, loading: false, error: null };
    case "FETCH_ERROR":
      return { ...state, loading: false, error: action.payload };
    default:
      return state;
  }
}

Mental model: state + action โ†’ reducer โ†’ new state.

6. Why Generics (<T>) Matter

Without generics, the code only works for Product[] and must be rewritten for every API. With generics, T is a type placeholder:

useFetch<Product[]>
useFetch<Category[]>
useFetch<User[]>

The same system adapts automatically.

7. The Full useReducer Flow

1. initialState
2. dispatch(FETCH_START)
3. reducer โ†’ loading = true
4. API call
5. dispatch(FETCH_SUCCESS)
6. reducer โ†’ data filled
7. UI updates

8. End-to-End Diagram

Component
   โ”‚
   โ–ผ
dispatch({ type: "FETCH_START" })
   โ”‚
   โ–ผ
Reducer โ†’ loading = true
   โ”‚
   โ–ผ
API Request
   โ”‚
   โ”œโ”€โ”€ SUCCESS โ†’ dispatch(FETCH_SUCCESS) โ†’ data = result
   โ”‚
   โ””โ”€โ”€ ERROR โ†’ dispatch(FETCH_ERROR) โ†’ error message

9. Senior-Level Insight

useReducer is not just state management. It is a finite state machine where actions are events, the reducer holds the transition rules, and the state is a snapshot of the system.

10. Why Payload Is Critical

Without a payload you can only say "something happened". With a payload you can transfer real data through events. FETCH_SUCCESS without a payload is useless; with a payload it becomes a meaningful state update.

11. Final Mental Model

  • ๐Ÿ“ฎ dispatch = send a message
  • ๐Ÿ“„ action = message + optional data (payload)
  • ๐Ÿ‘จโ€๐Ÿ’ผ reducer = decision maker
  • ๐Ÿงพ state = final result
  • <T> = a flexible type system for reuse

Example repository: react-learning-journey on GitHub