Use Effect
A multi-step code challenge to learn how useState works, how parent and child components share data, and how event handlers flow up while data flows down.
You’ll build a small Todo app from scratch. Each step adds one feature. Each step page has a prompt, hints, the React concepts in play, and a solution at the bottom you can scroll to if stuck.
What you’ll build
A TodoList with three components:
TodoList— owns the array of todos and the handlers that mutate it.TodoComposer— a controlled<input>+ button for adding new todos.Todo— renders a single todo with checkbox, edit/save toggle, and delete.
By the end, you’ll be able to add, toggle, edit, and delete todos.
What you’ll learn
- How
useStateactually behaves across renders. - The “data down, handlers up” pattern — children never reach into the parent’s state.
- Immutable updates with
{ ...todo }andarr.map/arr.filter. - How to decide whether state belongs in the parent or the child.
- How conditional rendering (
{cond ? A : B},{cond && A}) interacts with state.
Prerequisites — read these first
If any of these are unfamiliar, read them before starting. Each is short.
- What is JSX?
- From component to React element to DOM
useState- Component conventions
props.childrenpatterns- Destructuring and spread
How to work through this
- Set up a scratch React project (Vite + React, your existing one, anything that runs JSX).
- Read each step page top-to-bottom before looking at its Solution section.
- Try to write the code yourself. Run it. See what breaks.
- Only then scroll to Solution and compare.
- Read Why this works even if your solution matched — it explains the idea, not just the answer.
Source attribution
The “final code” this walkthrough builds toward came from a course exercise. It’s been lightly cleaned up:
- Imports use
import { useState } from 'react'(named imports, matching the rest of this wiki). - A dead
useStatein the originalTodocomponent has been removed (mentioned again in step 9). - Random integer IDs have been replaced with
crypto.randomUUID()(mentioned again in step 6).
The full final code is on Recap.
Steps
- Setup — three component files, what each owns.
- Rendering a static list —
.map(),key, no state yet. - Holding the list in state — convert array to
useState. - Extracting
<Todo />— props, single-responsibility. - Adding todos —
<TodoComposer />, lifting state up. - Toggling completed — object spread, immutable update.
- Deleting a todo —
.filter()pattern. - Editing a todo — local component state, conditional rendering.
- Recap — full final code, lessons learned.