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
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