State & Hooks
State is a component's memory. Hooks are the functions that give components state and other React features.
Why ordinary variables don't work
You might try to make a counter with a normal variable:
function Counter() {
let count = 0;
return (
<button onClick={() => count++}>
Clicked {count} times
</button>
);
}
This fails for two reasons. Local variables are reset every time the function runs, and changing one does not tell React to update the screen.
useState
The useState hook solves both problems. It gives you a value that React keeps between renders, and a setter function that updates the value and triggers a re-render:
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
);
}
Each <Counter /> you place on the page keeps its own separate count.
State with forms
A common pattern is to keep form inputs in state. This is called a controlled input:
function NameForm() {
const [name, setName] = useState("");
return (
<>
<input
value={name}
onChange={e => setName(e.target.value)}
placeholder="Your name"
/>
<p>Hello, {name || "stranger"}!</p>
</>
);
}
Updating objects and arrays
Treat state as immutable. Don't modify it in place; create a new copy instead:
// โ Wrong: mutates the existing array
todos.push(newTodo);
setTodos(todos);
// โ Right: creates a new array
setTodos([...todos, newTodo]);
// โ Right: copies an object with one field changed
setUser({ ...user, name: "Grace" });
Sharing state between components
When two components need the same data, move the state up to their closest common parent and pass it down as props. This is called lifting state up:
function App() {
const [query, setQuery] = useState("");
return (
<>
<SearchBox query={query} onChange={setQuery} />
<Results query={query} />
</>
);
}
Data flows down through props, and events flow up through callback functions. This one-way data flow makes React apps easier to reason about.
Other common hooks
| Hook | What it's for |
|---|---|
useState | Storing a value that changes over time and re-rendering when it does. |
useEffect | Syncing with something outside React, such as timers, subscriptions or browser APIs. |
useRef | Holding a value that doesn't trigger re-renders, or referencing a DOM element. |
useContext | Reading shared data (such as a theme or the logged-in user) without passing props through every level. |
useReducer | Managing more complex state with a reducer function. |
A quick look at useEffect
import { useState, useEffect } from "react";
function Clock() {
const [time, setTime] = useState(new Date());
useEffect(() => {
const id = setInterval(() => setTime(new Date()), 1000);
return () => clearInterval(id); // clean up when removed
}, []); // [] = run once, when the component first appears
return <p>{time.toLocaleTimeString()}</p>;
}
The rules of hooks
- Only call hooks at the top level of a component, never inside loops, conditions or nested functions.
- Only call hooks from React components or from your own custom hooks (functions whose names start with
use).
React relies on hooks being called in the same order on every render, which is why these rules exist. The official ESLint plugin checks them for you.