useState()
useState
is a React Hook that lets a component “remember” some information (called state). It returns two values: the current state, and the function you can use to update it.
const [state, setState] = useState(initialState);
Using state
You can add state to your component in three steps:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
You pressed me {count} times
</button>
);
}
Call useState
and pass the initial state to it. React will store the state that you passed, and give it back to you.
To change it, call the state setter function with the next state value. React will put that value into state instead.
Use the state for rendering by putting it into the JSX.
State is a component’s memory. A state variable lets you hold some information that changes over time and is specific to your component. Here, count
holds the number of clicks. However, you can keep any JavaScript value in state—for example, the current input text, the selected gallery image, or the contents of a shopping cart.
import { useState } from 'react'; export default function Counter() { const [count, setCount] = useState(0); function handleClick() { setCount(count + 1); } return ( <button onClick={handleClick}> You pressed me {count} times </button> ); }
Declaring a state variable
You can declare multiple state variables in a component. You must declare them at the top level of your component, outside of any conditions or loops. This component declares state variables called name
and age
:
import { useState } from 'react';
function Form() {
const [name, setName] = useState('Taylor');
const [age, setAge] = useState(28);
// ...
You get the latest state value for your JSX.
You get the function that lets you update the state.
You pass the value you want the state to be initially.
This allows your component to “remember” multiple independent things—for example, different form fields.
import { useState } from 'react'; export default function Form() { const [name, setName] = useState('Taylor'); const [age, setAge] = useState(28); return ( <> <input value={name} onChange={e => setName(e.target.value)} /> <button onClick={() => setAge(age + 1)}> Happy birthday! </button> <p>Hello, {name}. You are {age}.</p> </> ); }
The [
and ]
syntax here is called array destructuring and it lets you read values from an array. The array returned by useState
always has exactly two items—it’s a pair. By convention, name them like [thing, setThing]
.
Gotcha
Calling setState
only affects the next render and does not change state in the already running code:
function handleClick() {
console.log(count); // 0
setCount(count + 1); // Request a re-render with 1
console.log(count); // Still 0!
}
When not to use it
- Don’t use state when a regular variable works. State is only used to persist information between re-renders.
- Don’t add redundant state. If you can calculate something during render, you don’t need state for it.
Updating objects and arrays in state
You can hold objects and arrays in state, too. However, you should always replace objects in state rather than modify the existing ones. Updating objects and updating arrays describe common patterns that help avoid bugs.
import { useState } from 'react'; export default function MovingDot() { const [position, setPosition] = useState({ x: 0, y: 0 }); return ( <div onPointerMove={e => { setPosition({ x: e.clientX, y: e.clientY }); }} style={{ position: 'relative', width: '100vw', height: '100vh', }}> <div style={{ position: 'absolute', backgroundColor: 'red', borderRadius: '50%', transform: `translate(${position.x}px, ${position.y}px)`, left: -10, top: -10, width: 20, height: 20, }} /> </div> ) }
Try out some recipes
Recipe 1 of 4: Image gallery
import { useState } from 'react'; import { sculptureList } from './data.js'; export default function Gallery() { const [index, setIndex] = useState(0); const [showMore, setShowMore] = useState(false); let hasPrev = index > 0; let hasNext = index < sculptureList.length - 1; function handlePrevClick() { if (hasPrev) { setIndex(index - 1); } } function handleNextClick() { if (hasNext) { setIndex(index + 1); } } function handleMoreClick() { setShowMore(!showMore); } let sculpture = sculptureList[index]; return ( <> <button onClick={handlePrevClick} disabled={!hasPrev} > Previous </button> <button onClick={handleNextClick} disabled={!hasNext} > Next </button> <h2> <i>{sculpture.name} </i> by {sculpture.artist} </h2> <h3> ({index + 1} of {sculptureList.length}) </h3> <button onClick={handleMoreClick}> {showMore ? 'Hide' : 'Show'} details </button> {showMore && <p>{sculpture.description}</p>} <img src={sculpture.url} alt={sculpture.alt} /> </> ); }
Special cases
Passing the same value to setState
If you pass the current state to setState
, React will skip re-rendering the component:
setCount(count); // Won't trigger a re-render
This is a performance optimization. React uses the Object.is()
algorithm to compare the values.
Passing an updater function to setState
Instead of passing the next state itself, you may pass a function to setState
. Such a function, like c => c + 1
in this example, is called an “updater”. React will call your updater during the next render to calculate the final state.
function handleClick() {
setCount(123);
setCount(c => c + 1); // Result: 124
}
Usually, when you set state, you replace it.
But you can pass an updater function to transform it.
Your updater function will receive the pending state and should return the next state.
function handleClick() {
// 0 => 1
setCount(c => c + 1);
// 1 => 2
setCount(c => c + 1);
// 2 => 3
setCount(c => c + 1);
}
You get the latest state with the previously queued updates applied to it. For example, if count
was 0
and you call setCount(c => c + 1)
three times in a row, then the pending c
state will be 0
in the first updater, 1
in the second one, and 2
in the third one, and 3
is the final state.
You return the next state you want to see on the screen.
Updaters are a bit verbose but sometimes they come in handy. They let you access the pending state rather than the last rendered state. This is helpful if you want to queue multiple updates on top of each other.
import { useState } from 'react'; export default function Counter() { const [count, setCount] = useState(0); function handleClick() { setCount(c => c + 1); setCount(c => c + 1); setCount(c => c + 1); } return ( <> <h1>{count}</h1> <button onClick={handleClick}> Add 3 </button> </> ) }
Updater functions run during rendering. This is why they should be pure functions. In other words, your updater functions should only calculate and return the next state. They should not try to “do” things or set state.
If you don’t have a particular reason to use an updater, you can stick with passing the next state directly.
Gotcha
You need to be careful when returning an object from an arrow function. This doesn’t work:
setForm(f => {
name: f.name.toUpperCase()
});
This code doesn’t work because JavaScript considers => {
to be a function body rather than a returned object. Since it is a function body, you have to write an explicit return
statement:
setForm(f => {
return {
name: f.name.toUpperCase()
};
});
Alternatively, you have to add parentheses around your object:
setForm(f => ({
name: f.name.toUpperCase()
}));
This is an unfortunate language quirk of JavaScript, and is not specific to React.
Gotcha
Because setState()
with a function argument has this special meaning, you can’t put a function in state like this:
setState(myFunction);
If you really need to put a function in state (which is rare), you can do this instead:
setState(() => myFunction);
Passing an initializer function to useState
The initial state that you pass to useState
as an argument is only used for the initial render. For next renders, this argument is ignored. If creating the initial state is expensive, it is wasteful to create and throw it away many times. You can pass a function to useState
to calculate the initial state. React will only run it during the initialization.
function TodoList() {
const [text, setText] = useState('');
const [todos, setTodos] = useState(
() => createInitialTodos()
);
// ...
Most often, you will provide the initial state during render.
But you can also give React a function that calculates the initial state instead. React will only call that function when initializing the component, and won’t call it again.
This is a performance optimization. You can use it to avoid creating large objects or arrays on re-renders.
import { useState } from 'react'; function createInitialTodos() { const initialTodos = []; for (let i = 0; i < 50; i++) { initialTodos.push({ id: i, text: 'Item #' + i }); } return initialTodos; } export default function TodoList() { const [todos, setTodos] = useState( () => createInitialTodos() ); return ( <ul> {todos.map(item => ( <li key={item.id}> {item.text} </li> ))} </ul> ); }
Initializer functions run during rendering. This is why they should be pure functions. In other words, your initializer functions should only calculate and return the initial state. They should not try to “do” things or set state.
If you don’t have a particular reason to use an initializer, you can stick with passing the initial state directly.