Holding the List in State
The visible result won’t change. The mechanism does. Move the array from a plain const into a useState call so that — once we add handlers in later steps — the UI re-renders when the list changes.
Goal
The todos array lives in useState. The rendered output is identical to step 3, but the list is now ready to be mutated.
React concepts you’ll use
useState— seeuseState- Initial state via the
useStateargument - Why state at all (a
constdoesn’t trigger re-renders)
Implementation
-
useStateis imported fromreact - The todos array is held in
useStateinstead of a plainconst - The initial value of state is the same array from step 3
- The setter is destructured but not yet called (later steps use it)
- The rendered output is identical to step 3
Try it
Convert the hardcoded array into useState. The page should look unchanged.
Solution
// src/components/TodoList.jsx
import { useState } from 'react';
import Todo from './Todo';
import TodoComposer from './TodoComposer';
export default function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, label: 'Learn React', completed: false },
{ id: 2, label: 'Learn Next.js', completed: false },
{ id: 3, label: 'Learn React Query', completed: false },
]);
return (
<ul>
<TodoComposer />
{todos.map((todo) => (
<Todo key={todo.id} todo={todo} />
))}
</ul>
);
}Why this works
useState ties a piece of memory to the component across renders — a plain let would reset every render. The argument is only used on the first render; after that, the value comes from whatever the setter was last called with. Calling the setter schedules a re-render; it doesn’t change todos in the current render, and it doesn’t mutate. Every state update from here on produces a new array ([...todos, x], .filter, .map), never an in-place mutation — React detects changes by reference equality, so a new reference is non-negotiable.
What you should see now
The same three list items as step 3. Open React DevTools and click on TodoList — you should now see a State entry showing the array. That’s the new memory cell useState carved out.
The list is still effectively read-only. The next step extracts a clean <Todo /> component so we have somewhere to add interactivity in steps 6+.