Runnable lesson · press Try it on the examples to edit and run them here.
useState is the first hook most people learn, and the one you will use most. A hook is a special function whose name starts with use that lets a function component use a React feature: state, effects, context and more.
Syntax
const [value, setValue] = useState(initialValue);
useState(initialValue)returns an array of two items.- The first item is the current state; the second is the function that updates it.
- You choose the names with array destructuring. The convention is
thingandsetThing.
function App() {
const [name, setName] = React.useState("Ada");
return (
<div>
<p>Hello, {name}</p>
<button onClick={() => setName("Grace")}>Rename</button>
</div>
);
}
If array destructuring is new to you, see Array Destructuring.
Any type of value
State can hold any JavaScript value:
function App() {
const [name, setName] = React.useState("Ada"); // string
const [age, setAge] = React.useState(36); // number
const [admin, setAdmin] = React.useState(true); // boolean
const [skills, setSkills] = React.useState(["JS", "CSS"]); // array
function changeAll() {
setName("Grace");
setAge(85);
setAdmin(false);
setSkills([...skills, "COBOL"]);
}
return (
<div>
<p>{name}, {age}, {String(admin)}, {skills.length} skills</p>
<button onClick={changeAll}>Change all</button>
</div>
);
}
For objects and arrays you must always replace the value with a new one rather than change it. That is covered in Updating Objects in State and Updating Arrays in State.
The initial value is used only once
useState(0) uses 0 only on the first render. On later renders React ignores the argument and gives you the current state.
The lazy initializer
If the initial value is expensive to compute (reading and parsing data, a big calculation), pass a function. React calls it only on the first render:
function loadItems() {
console.log("loadItems ran");
return ["a", "b", "c"];
}
function App() {
// loadItems() would run on EVERY render; the function form runs only once
const [items] = React.useState(loadItems);
const [n, setN] = React.useState(0);
return (
<div>
<p>Items: {items.length}</p>
<button onClick={() => setN(n + 1)}>Re-render ({n})</button>
</div>
);
}
Click the button a few times and look at the console: loadItems ran appears only once. Note that you pass the function itself (loadItems), not a call (loadItems()).
Rules for hooks
Hooks must be called in the same order on every render. So:
- Call hooks at the top level of a component or custom hook.
- Never call them inside conditions, loops or nested functions.
- Call them only from React function components or custom hooks.
Rules of Hooks explains why, and the Vite template has a lint rule that catches mistakes.
A toggle and a counter
Two small patterns that appear everywhere:
function App() {
const [open, setOpen] = React.useState(false);
return (
<div>
<button onClick={() => setOpen(!open)}>Toggle menu</button>
<p>Menu is {open ? "open" : "closed"}</p>
</div>
);
}
Common mistakes
- Changing state directly:
count = count + 1orcount++does nothing. Always call the setter. - Calling the setter during rendering (
setCount(1)in the body of the component) causes an infinite loop. Call it in event handlers or effects. - Expecting the new value right after calling the setter. The variable still holds the old value in that function. See State as a Snapshot.
- Storing too much. Do not store anything you can compute.
Key points
const [value, setValue] = useState(initial)creates state; call the setter to change it.- The initial value is used once; pass a function for expensive initial values.
- Call hooks at the top level, never conditionally.