Skip to content
    SeaCode
    Learning tracksCode ArenaWorkshops & EventsInstructors
    Log inSign up
    ⚛React

    Components and JSX

    • Why React?
    • JSX rules
    • Props
    • Rendering lists and keys
    • Conditional rendering

    State and events

    • Handling events
    • useState: a component's memory
    • Objects and arrays in state
    • Forms and controlled inputs
    • Lifting state up

    Effects and essential hooks

    • useEffect
    • Fetching data
    • useRef
    • Context: data for the whole tree
    • useReducer

    Professional React

    • Writing your own hooks
    • Performance: memo, useMemo and useCallback
    • Suspense and error boundaries
    • What's new in React 19
    • Thinking in React
    • Project: a complete to-do app
    React

    Professional React · Lesson 21 of 21

    Project: a complete to-do app

    Add, delete, complete, filter and count, step by step.

    15 minutes3 games

    We'll build a study to-do app in three stages, each a self-checking exercise. By the end you'll have: adding tasks, marking them done, deleting them, filtering (all / active / done), and a counter.

    Here's the finished version, try it:

    React
    function App() {
      const [tasks, setTasks] = useState([
        { id: 1, text: "Watch the useState lesson", done: true },
        { id: 2, text: "Solve 3 exercises", done: false },
      ])
      const [text, setText] = useState("")
      const [filter, setFilter] = useState("all")
    
      function add(e) {
        e.preventDefault()
        if (!text.trim()) return
        setTasks([...tasks, { id: Date.now(), text: text.trim(), done: false }])
        setText("")
      }
      const toggle = (id) => setTasks(tasks.map((t) => (t.id === id ? { ...t, done: !t.done } : t)))
      const remove = (id) => setTasks(tasks.filter((t) => t.id !== id))
      const visible = tasks.filter((t) => (filter === "all" ? true : filter === "done" ? t.done : !t.done))
      const left = tasks.filter((t) => !t.done).length
    
      return (
        <div style={{ maxWidth: 360 }}>
          <form onSubmit={add}>
            <input value={text} onChange={(e) => setText(e.target.value)} placeholder="New task" />
            <button>Add</button>
          </form>
          <ul style={{ paddingInlineStart: 18 }}>
            {visible.map((t) => (
              <li key={t.id}>
                <label style={{ textDecoration: t.done ? "line-through" : "none" }}>
                  <input type="checkbox" checked={t.done} onChange={() => toggle(t.id)} /> {t.text}
                </label>{" "}
                <button onClick={() => remove(t.id)}>✕</button>
              </li>
            ))}
          </ul>
          <p>{left} left · {["all", "active", "done"].map((f) => (
            <button key={f} onClick={() => setFilter(f)} disabled={filter === f}>{f}</button>
          ))}</p>
        </div>
      )
    }
    tasks[{ id, text, done }, …]state
    text""state (input)
    filter"all"state
    visibletasks filteredcomputed
    leftcount of !donecomputed

    Only three pieces of state; the rest is computed.

    Stage 1: adding

    Exercise

    Finish the form: on submit, add the task to the list and clear the input; never add an empty task. The checker types Read and presses Add.

    The page should show:
    Add
    Read
    1 tasks
    React
    function App() {
      const [tasks, setTasks] = useState([])
      const [text, setText] = useState("")
    
      function add(e) {
        e.preventDefault()
        // add the task here
      }
    
      return (
        <form onSubmit={add}>
          <input value={text} onChange={(e) => setText(e.target.value)} />
          <button>Add</button>
          <ul>{tasks.map((t) => <li key={t.id}>{t.text}</li>)}</ul>
          <p>{tasks.length} tasks</p>
        </form>
      )
    }

    Stage 2: completing and counting

    Exercise

    Each task has a checkbox that flips done, and the last line shows N left (tasks not done). The checker ticks the first task.

    The page should show:
    Read
    Practice
    Quiz
    1 left
    React
    function App() {
      const [tasks, setTasks] = useState([
        { id: 1, text: "Read", done: false },
        { id: 2, text: "Practice", done: false },
        { id: 3, text: "Quiz", done: true },
      ])
    
      return (
        <div>
          <ul>
            {tasks.map((t) => (
              <li key={t.id}><input type="checkbox" checked={t.done} readOnly /> {t.text}</li>
            ))}
          </ul>
          <p>? left</p>
        </div>
      )
    }

    Stage 3: filtering

    Exercise

    Add three filter buttons: all, active and done. The list shows tasks for the chosen filter. The checker presses done, so only Quiz should show.

    The page should show:
    all active done
    Quiz
    React
    const TASKS = [
      { id: 1, text: "Read", done: false },
      { id: 2, text: "Practice", done: false },
      { id: 3, text: "Quiz", done: true },
    ]
    
    function App() {
      const [filter, setFilter] = useState("all")
      const visible = TASKS
      return (
        <div>
          {["all", "active", "done"].map((f) => (
            <button key={f} className={f}>{f}</button>
          ))}
          <ul>{visible.map((t) => <li key={t.id}>{t.text}</li>)}</ul>
        </div>
      )
    }

    Important

    Next steps in a real project: persist the tasks (in localStorage via a custom hook like useLocalStorage, or a database like Supabase), move the task logic into useReducer, and split the UI into TaskForm, TaskList, TaskItem and Filters.

    Interview question

    A common interview task: "build a to-do app in 30 minutes". What is the interviewer looking for?

    Learn more from

    • react.dev: Updating arrays in statereact.dev
    • react.dev: Managing statereact.dev
    • MDN: React to-do list tutorialdeveloper.mozilla.org

    Played 0 of 3 games

    Sign in to save your progress and earn XP.

    PreviousThinking in React🎉 خلّصت React!
    SeaCode

    Learn to code for real, online or at our branch in front of Cairo University, Giza.

    Learn

    • All courses
    • Interactive tutorials
    • Interview practice problems
    • Mock interview
    • Learning tracks
    • Code Arena
    • Workshops & Events
    • Knowledge library
    • Team projects

    Your account

    • Log in
    • Sign up
    • Learning plan
    • Certificates
    • Site map

    Contact us

    • WhatsApp
    • 010 9094 1104
    • In front of Cairo University, Giza

    © 2026 SeaCode. All rights reserved.