Filters, summary, ship it
The filter feature is pure derived state. filter holds "all", "active", or "done", and a helper picks the visible slice during render:
function FilterBar({ filter, onFilterChange }) { return ( <div> {["all", "active", "done"].map(f => ( <button key={f} disabled={f === filter} onClick={() => onFilterChange(f)} > {f} </button> ))} </div> ); }
Even the three buttons come from a map, with the string itself as a perfectly stable key. Disabling the active button is conditional rendering applied to an attribute.
Code exercise · javascript
Your turn, the last missing piece. Write visibleTasks(tasks, filter): "active" returns not-done tasks, "done" returns done tasks, anything else returns all of them.
The finished app, top to bottom
function TaskApp() { const [tasks, setTasks] = useState([]); const [filter, setFilter] = useState("all"); const [text, setText] = useState(""); const visible = visibleTasks(tasks, filter); const left = tasks.filter(t => !t.done).length; function handleAdd(e) { e.preventDefault(); if (text.trim() === "") return; setTasks(ts => [...ts, { id: Date.now(), title: text.trim(), done: false }]); setText(""); } return ( <div> <h1>Tasks</h1> <form onSubmit={handleAdd}> <input value={text} onChange={e => setText(e.target.value)} /> <button>Add</button> </form> <FilterBar filter={filter} onFilterChange={setFilter} /> <TaskList tasks={visible} onToggle={id => setTasks(ts => toggleTask(ts, id))} onDelete={id => setTasks(ts => deleteTask(ts, id))} /> <p>{left === 0 ? "All done!" : left + " task(s) left"}</p> </div> ); }
Read it slowly, you can now account for every line: three pieces of minimal state, two derived values, a controlled form, pure update helpers behind functional setters, filtered list rendering with stable keys, and a conditional summary. That is a complete, correct React application.
Quiz
The user is viewing the "done" filter and unchecks the last done task. Walk the render loop: what happens on screen, with no extra code?
Problem
Final callback. The task tracker never once calls document.getElementById or textContent, yet the screen always matches the data. Which unit-1 idea, in a few words, makes that possible?