On this page
State
State is data that changes over time and causes components to re-render when updated.
useState Hook
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
Functional Updates
When new state depends on previous state, use a function:
// Can be stale in rapid clicks
setCount(count + 1);
// Always uses latest state
setCount(prev => prev + 1);
setCount(prev => prev + 1); // Correctly adds 2
State with Objects
Never mutate state directly — create a new object:
function ProfileForm() {
const [user, setUser] = useState({ name: '', email: '' });
const handleChange = (e) => {
const { name, value } = e.target;
setUser(prev => ({ ...prev, [name]: value }));
};
return (
<form>
<input name="name" value={user.name} onChange={handleChange} />
<input name="email" value={user.email} onChange={handleChange} />
</form>
);
}
State with Arrays
function TodoList() {
const [todos, setTodos] = useState([]);
const addTodo = (text) => {
setTodos(prev => [...prev, { id: Date.now(), text, done: false }]);
};
const toggleTodo = (id) => {
setTodos(prev => prev.map(todo =>
todo.id === id ? { ...todo, done: !todo.done } : todo
));
};
const removeTodo = (id) => {
setTodos(prev => prev.filter(todo => todo.id !== id));
};
}
Lifting State Up
Share state between sibling components via a common parent:
function TemperatureInput({ label, temperature, onChange }) {
return (
<div>
<label>{label}</label>
<input value={temperature} onChange={e => onChange(e.target.value)} />
</div>
);
}
function Calculator() {
const [celsius, setCelsius] = useState('');
const fahrenheit = celsius ? (parseFloat(celsius) * 9/5 + 32).toFixed(1) : '';
return (
<div>
<TemperatureInput
label="Celsius"
temperature={celsius}
onChange={setCelsius}
/>
<p>Fahrenheit: {fahrenheit}°F</p>
</div>
);
}
Multiple State Variables
Prefer multiple useState calls over one large object:
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [loading, setLoading] = useState(false);
State Initialization
Lazy initialization for expensive computations:
const [data, setData] = useState(() => {
const saved = localStorage.getItem('data');
return saved ? JSON.parse(saved) : defaultData;
});
State belongs to the component that owns the data — lift it up only as high as needed.