REACT FUNDAMENTALS · BYTE 01
Component tree
One App component describes the first task list.Why a static task list is not enough
The current to-do list is only a display. A learner should be able to type a task, click Add, and tick a checkbox. These actions change what appears on the screen. For that we need state and event handlers.
What you will build: Milestone 3 of a small browser-based To-Do List. No backend or advanced architecture is needed.
Adding and completing tasks
Imagine an editable whiteboard: the list is what everyone sees, and the marker actions change it. In React, state is the current remembered list and setTasks is how we request an update. React then renders the new list.
State, events and immutable updates
useState(initialTasks) returns [tasks, setTasks]: the current value and a setter. The typed input has another state value, draft. A controlled input gets value={draft} and updates through onChange. An event handler such as onClick={addTask} runs after a click. To add a task, create a new array with [...current, newTask]. To toggle one task, use .map() and copy only the matching object. Never mutate the old array in place. pending is derived data and should be calculated using .filter() rather than stored separately.
Follow an Add Task click
Before the diagram: A local variable resets when a component runs again. React state remembers a value between renders. An event handler tells React when to update that remembered value.
CONCEPT → CODE → OUTPUT · BYTE 02
From data to a reusable TaskItem
Select any stage to inspect what React or JavaScript does.
Stores task objects with an id and title
Predict the output: If the list has two tasks and you add “Buy notebook,” the next render shows three tasks. Changing an object directly without calling a setter is not a reliable way to update React state.
-
Learner types
Buy notebook→onChangecallssetDraft. -
React renders the new input value.
-
Learner clicks Add →
addTaskchecks for empty text. -
setTaskscreates a new array with the added task. -
React renders an extra row and clears the input.
-
Checking a box updates the matching task and pending count.
Explore task-state updates
REACT VISUAL LAB · BYTE 01
Component tree
One App component describes the first task list.
My Task Manager
Learn React one small step at a time.
- Read React notes
- Practise JSX
This is a concept simulator. Build the actual React code in the walkthrough below.
Check the idea: Answer this question before coding.
INTERACTIVE REACT LAB · BYTE 01
Which React component describes the Task Manager screen?
Build the interactive Task Manager
Our learning project: A tiny Task Manager, not an enterprise support system. The app grows gradually across the five Bytes; each code step below tells you exactly which file to edit.
Milestone 3: React useState and Events: Add and Complete Tasks.
Step 1: Extend the task data model
Where: src/components/TaskItem.tsx
01export type Task = { id: number; title: string; completed: boolean };02export default function TaskItem({03task, onToggle04}: {05task: Task; onToggle: (id: number) => void06}) {07return (08<li>09<label>10<input type="checkbox" checked={task.completed}11onChange={() => onToggle(task.id)} />12<span style={{ textDecoration: task.completed ? 'line-through' : 'none' }}>13{task.title}14</span>15</label>16</li>17);18}
Expected screen/result: Each row displays a checkbox and completed styling.
Step 2: Update starting data
Where: src/data/tasks.ts
01import type { Task } from '../components/TaskItem';02export const initialTasks: Task[] = [03{ id: 1, title: 'Read React notes', completed: false },04{ id: 2, title: 'Practise JSX', completed: false }05];
Expected screen/result: Each task has a completed flag.
Step 3: Add task state and events
Where: src/App.tsx
01import { useState } from 'react';02import TaskItem, { type Task } from './components/TaskItem';03import { initialTasks } from './data/tasks';0405export default function App() {06const [tasks, setTasks] = useState<Task[]>(initialTasks);07const [draft, setDraft] = useState('');0809function addTask() {10const title = draft.trim();11if (!title) return;12setTasks(current => [13...current,14{ id: Date.now(), title, completed: false }15]);16setDraft('');17}1819function toggleTask(id: number) {20setTasks(current => current.map(task =>21task.id === id ? { ...task, completed: !task.completed } : task22));23}2425return (26<main>27<h1>My Task Manager</h1>28<label htmlFor="new-task">New task</label>29<input id="new-task" value={draft}30onChange={event => setDraft(event.target.value)} />31<button type="button" onClick={addTask}>Add</button>32<p>{tasks.filter(task => !task.completed).length} pending</p>33<ul>{tasks.map(task =>34<TaskItem key={task.id} task={task} onToggle={toggleTask} />35)}</ul>36</main>37);38}
Expected screen/result: Typing and clicking Add creates a new task; checking it updates pending count and completion state.
Project checkpoint: Add and complete tasks.
Common useState mistakes
onClick={addTask()}: This executes during rendering; passonClick={addTask}.- Mutating arrays:
tasks.push()does not create the next state snapshot. - Empty items: Use
trim()and return early. - Separate pending state: Duplicated state can disagree with the actual tasks.
- Refresh loses tasks: Correct for now; Byte 04 adds persistence.
Where UI state matters
React uses the same underlying idea in many applications: a product list, a shopping basket, a learning progress page, or a dashboard. We are using task management only to keep the learning journey simple.
What happens when state changes
| What a learner sees | What React or the browser does |
|---|---|
| updated checkbox | Type → setDraft → render input; click Add → setTasks → render new row; click Complete → setTasks |
| A task list ready for interaction | React creates output from component inputs and state |
Create a Delete action
Challenge 1: Add two tasks. Confirm each appears once and input clears.
Challenge 2: Mark the first item completed, then reopen it. Observe the pending count.
Challenge 3: Implement Delete using filter() and check that only the selected task disappears.
Explain it: Why do we create a new array rather than call tasks.push()?
Interview Bit
Question: Why should React state be updated with a setter instead of changing the variable directly?
Simple answer: React state stores data between renders. Calling setTasks with a new array tells React to render the updated list. Mutating the old array with push can prevent React from detecting the intended update.
How to explain it: Point to the related code in the To-Do List and describe what appears in the browser.
Key Takeaways
- useState provides a value and a setter.
- State retains values between component renders.
- Controlled inputs use state for displayed values.
- Event handlers run in response to interaction.
- State updates request a new render.
- Create new arrays and objects instead of mutating state.
- Calculate the pending count from tasks, not duplicate state.
- Byte 03 adds and completes tasks interactively.
Knowledge Check
Review these common questions before attempting the scored quiz.
- Why use the setter? React needs to know the state changed.
- Should pending count be another state variable? No, derive it from tasks.
- Does the task survive refresh? Not yet; Byte 04 addresses that.
PRACTISE AND CHECK
Knowledge Check · 5 questions
Choose one answer for every question. Submit to see your score and explanations.
0 of 5 answered