React · Lesson 3 of 7
State with useState
Give components memory and update it without mutating.
- Beginner
- 16 min read
- 3 objectives
Before this lessonLesson 2: JSX and Components
What you will learn
- Use useState
- Update objects and arrays immutably
- Lift state up
A regular variable inside a component is reset every time the component runs and, crucially, changing it does not tell React to redraw. State solves both problems: it is memory that React preserves between renders, and updating it triggers a new render.
useState
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
);
}useState(0) returns the current value and a setter. The argument is only the initial value. Calling setCount schedules a re-render with the new value. Hooks must be called at the top level of a component, never inside loops, conditions or nested functions.
State updates are snapshots
Within one render, count is a fixed number. Calling setCount(count + 1) three times adds only one, because each call sees the same count. When the next value depends on the previous one, pass an updater function.
function addThree() {
setCount((c) => c + 1);
setCount((c) => c + 1);
setCount((c) => c + 1); // total +3
}Never mutate state
Treat state as immutable. Create a new object or array instead of editing the existing one; otherwise React cannot detect the change.
const [user, setUser] = useState({ name: "Ada", age: 36 });
const [todos, setTodos] = useState(["write", "test"]);
// objects: copy then override
setUser({ ...user, age: 37 });
// arrays
setTodos([...todos, "deploy"]); // add
setTodos(todos.filter((t) => t !== "test")); // remove
setTodos(todos.map((t) => (t === "write" ? "edit" : t))); // changeLifting state up
When two components need the same data, move the state to their closest common parent and pass it down as props, along with functions to change it. This keeps one source of truth.
function Parent() {
const [query, setQuery] = useState("");
return (
<>
<SearchBox value={query} onChange={setQuery} />
<Results query={query} />
</>
);
}Deriving values
Do not store something in state if you can compute it from existing state or props. Storing fullName alongside first and last creates a chance for them to disagree; compute it during render instead.
// Write your solution here
