DOM & Browsermedium

Build a small todo list with useReducer

Implement add (text from a controlled field), toggle `done`, and delete. Keep todos in an array of `{ id, text, done }`. Use `useReducer` for all mutations. Show counts of active (not done) items.

Asked at Meta, Airbnb, Apple

#react#useReducer#lists#state management

Answer

const initial = { todos: [], draft: '' }

function reducer(state, action) {
  switch (action.type) {
    case 'draft':
      return { ...state, draft: action.value }
    case 'add': {
      const text = state.draft.trim()
      if (!text) return state
      const todo = { id: crypto.randomUUID(), text, done: false }
      return { draft: '', todos: [...state.todos, todo] }
    }
    case 'toggle':
      return {
        ...state,
        todos: state.todos.map((t) =>
          t.id === action.id ? { ...t, done: !t.done } : t,
        ),
      }
    case 'remove':
      return {
        ...state,
        todos: state.todos.filter((t) => t.id !== action.id),
      }
    default:
      return state
  }
}

function TodoList() {
  const [state, dispatch] = React.useReducer(reducer, initial)
  const activeCount = state.todos.filter((t) => !t.done).length

  return (
    <div>
      <form
        onSubmit={(e) => {
          e.preventDefault()
          dispatch({ type: 'add' })
        }}
      >
        <input
          value={state.draft}
          onChange={(e) => dispatch({ type: 'draft', value: e.target.value })}
        />
        <button type="submit">Add</button>
      </form>
      <p>Active: {activeCount}</p>
      <ul>
        {state.todos.map((t) => (
          <li key={t.id}>
            <label>
              <input
                type="checkbox"
                checked={t.done}
                onChange={() => dispatch({ type: 'toggle', id: t.id })}
              />
              {t.text}
            </label>
            <button type="button" onClick={() => dispatch({ type: 'remove', id: t.id })}>
              Delete
            </button>
          </li>
        ))}
      </ul>
    </div>
  )
}

// Interview talking points:
// - useReducer shines when many actions touch the same state shape.
// - Immutability: map/filter/spread, never mutate todos[i].done in place.
// - Keys: stable id, never array index, if list reorder or deletes exist.

Source: common React screen interview

Practise more DOM & Browser questions →