useReducer Hook

useReducer Hook

useReducer is an alternative to useState for managing complex state. It follows the Redux pattern: you dispatch actions, and a reducer function decides how state changes. Use it when:

  • State has multiple sub-values that update together
  • The next state depends on the previous in complex ways
  • You want to keep state logic easy to test independently

Syntax

const [state, dispatch] = useReducer(reducer, initialState);

// reducer: (currentState, action) => newState
// dispatch: call to send an action to the reducer

Complete Example โ€” Task Manager

const initialState = {
  tasks:   [],
  filter:  "all",    // "all" | "active" | "done"
  loading: false,
};

function taskReducer(state, action) {
  switch (action.type) {
    case "ADD_TASK":
      return {
        ...state,
        tasks: [...state.tasks, { id: Date.now(), text: action.payload, done: false }],
      };
    case "TOGGLE_TASK":
      return {
        ...state,
        tasks: state.tasks.map(t =>
          t.id === action.payload ? { ...t, done: !t.done } : t
        ),
      };
    case "DELETE_TASK":
      return { ...state, tasks: state.tasks.filter(t => t.id !== action.payload) };
    case "SET_FILTER":
      return { ...state, filter: action.payload };
    case "CLEAR_DONE":
      return { ...state, tasks: state.tasks.filter(t => !t.done) };
    default:
      throw new Error(`Unknown action: ${action.type}`);
  }
}

function TaskManager() {
  const [state, dispatch] = useReducer(taskReducer, initialState);

  const visibleTasks = state.tasks.filter(t => {
    if (state.filter === "active") return !t.done;
    if (state.filter === "done")   return  t.done;
    return true;
  });

  return (
    <div>
      <input
        onKeyDown={e => {
          if (e.key === "Enter" && e.target.value.trim()) {
            dispatch({ type: "ADD_TASK", payload: e.target.value.trim() });
            e.target.value = "";
          }
        }}
        placeholder="Add task and press Enter"
      />

      {["all","active","done"].map(f => (
        <button key={f} onClick={() => dispatch({ type: "SET_FILTER", payload: f })}>
          {f}
        </button>
      ))}

      <ul>
        {visibleTasks.map(task => (
          <li key={task.id}>
            <input type="checkbox" checked={task.done}
              onChange={() => dispatch({ type: "TOGGLE_TASK", payload: task.id })} />
            {task.text}
            <button onClick={() => dispatch({ type: "DELETE_TASK", payload: task.id })}>โœ•</button>
          </li>
        ))}
      </ul>

      <button onClick={() => dispatch({ type: "CLEAR_DONE" })}>Clear completed</button>
    </div>
  );
}

๐Ÿง  Test Yourself

What does the dispatch function do?