State & useState
Learn how React components remember things using state, and how the useState hook lets you add interactivity to your apps

Pixl presents
A component that forgets everything on every click is useless. useState gives it a memory. You're welcome.A component that forgets everything on every click is useless. useState gives it a memory. You're welcome.

So far your components only show static stuff. Real apps have to respond to people, though. A counter that goes up when you click it. A form that keeps track of what you type. A toggle that flips between light and dark mode. Every one of those needs state.
What is State?#
State is data that can change over time, and when it changes, your component re-renders. Think of it as your component's memory... it remembers values from one render to the next.
Here's the big difference between a regular variable and state.
// This does NOT work - the UI won't update
function BrokenCounter() {
let count = 0;
return (
<button onClick={() => { count += 1; }}>
Clicked {count} times
</button>
);
}Click that button and count really does change in memory. React just has NO idea it happened. So the screen sits there at "Clicked 0 times" forever (super frustrating the first time you hit it). Regular variables don't trigger re-renders.
Enter useState#
The useState hook is how React gives a component memory. It hands you back two things, the current value and a function to update it.
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
);
}Now when you click the button, setCount basically tells React "hey, the count changed!" React re-renders the component with the new value and the screen updates. Donezo.
Breaking Down useState#
Step 1. Import the Hook#
Before you can use useState, you have to import it from React.
import { useState } from 'react';Step 2. Call useState with an Initial Value#
Pass in the starting value as an argument. This value only matters on the very first render.
const [count, setCount] = useState(0); // starts at 0
const [name, setName] = useState(''); // starts as empty string
const [items, setItems] = useState([]); // starts as empty array
const [user, setUser] = useState(null); // starts as nullStep 3. Use Destructuring to Get the Value and Setter#
useState returns an array with exactly two items in it. You can name them whatever you want, but the convention everybody follows is [thing, setThing].
const [isVisible, setIsVisible] = useState(true);
const [score, setScore] = useState(100);
const [email, setEmail] = useState('');Updating State#
To change state, you call the setter function and hand it the new value.
function ToggleButton() {
const [isOn, setIsOn] = useState(false);
return (
<button onClick={() => setIsOn(!isOn)}>
{isOn ? 'ON' : 'OFF'}
</button>
);
}When you call setIsOn(!isOn), React schedules a re-render. Your component function runs again, useState hands back the new value, and the UI updates to match.
Updating State Based on Previous Value#

When your new state depends on the old state, use the functional form of the setter.
// Less reliable (might use stale value):
setCount(count + 1);
// More reliable (always uses the latest value):
setCount(prev => prev + 1);The functional form gets handed the most current value as an argument. That matters when a bunch of updates happen quickly or inside event handlers, where the plain version can grab a stale value without you noticing.
Multiple State Variables#
A component can have as many useState calls as it needs. There's no limit.
function SignupForm() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [age, setAge] = useState(0);
const [agreedToTerms, setAgreedToTerms] = useState(false);
return (
<form>
<input value={name} onChange={e => setName(e.target.value)} />
<input value={email} onChange={e => setEmail(e.target.value)} />
<input
type="number"
value={age}
onChange={e => setAge(Number(e.target.value))}
/>
<label>
<input
type="checkbox"
checked={agreedToTerms}
onChange={e => setAgreedToTerms(e.target.checked)}
/>
I agree to the terms
</label>
</form>
);
}State with Objects and Arrays#
You can store objects and arrays in state too. Just remember you have to create a new object or array whenever you update it, instead of changing (mutating) the one you already have.
function TodoList() {
const [todos, setTodos] = useState([]);
function addTodo(text) {
// Create a new array with the existing items plus the new one
setTodos([...todos, { id: Date.now(), text }]);
}
function removeTodo(id) {
// Create a new array without the removed item
setTodos(todos.filter(todo => todo.id !== id));
}
return (
<div>
<button onClick={() => addTodo('New task')}>Add</button>
{todos.map(todo => (
<p key={todo.id}>
{todo.text}
<button onClick={() => removeTodo(todo.id)}>Delete</button>
</p>
))}
</div>
);
}TL;DR#
- State is data that changes over time and triggers a re-render when it's updated
useStatereturns an array,[currentValue, setterFunction]- Always update state with the setter function (never change it directly)
- Use the functional form (
prev => prev + 1) when the new value depends on the old one - One component can have several
useStatecalls for different pieces of data - When you update objects or arrays, make a new copy instead of mutating the old one
What's Next?#
Your components can remember and update data now. Next you'll learn how to show or hide things based on that data. That's conditional rendering, which is just a fancy way of saying "show different UI depending on what's in state"...
This lesson ends with a short activity.
