State with useState
State is what makes React components interactive. While props are passed from parent to child (and are read-only), state is managed within a component and can change over time.
What is State?
State is data that:
- Belongs to a specific component
- Can change over time (usually in response to user actions)
- Causes the component to re-render when it changes
Think of state as a component's memory - it remembers things between renders.
The useState Hook
React provides the useState hook to add state to functional components:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
Breaking it Down
const [count, setCount] = useState(0);
useState(0)- Initialize state with value0count- The current state valuesetCount- Function to update the state- This uses array destructuring to get both values
Updating State
Basic Updates
// Set to a specific value
setCount(5);
// Set based on current value
setCount(count + 1);
Functional Updates
When the new state depends on the previous state, use a function:
// ✅ Correct - uses functional update
setCount(prevCount => prevCount + 1);
// ❌ Risky - might use stale value
setCount(count + 1);
This is especially important when updates might be batched or when updating state multiple times quickly.
State with Different Data Types
Strings
const [name, setName] = useState('');
<input
value={name}
onChange={(e) => setName(e.target.value)}
/>
Booleans
const [isVisible, setIsVisible] = useState(false);
<button onClick={() => setIsVisible(!isVisible)}>
Toggle
</button>
Objects
const [user, setUser] = useState({ name: '', email: '' });
// ✅ Correct - spread existing properties
setUser({ ...user, name: 'Alice' });
// ❌ Wrong - loses the email property
setUser({ name: 'Alice' });
Arrays
const [items, setItems] = useState([]);
// Add item
setItems([...items, newItem]);
// Remove item
setItems(items.filter(item => item.id !== idToRemove));
// Update item
setItems(items.map(item =>
item.id === id ? { ...item, done: true } : item
));
Multiple State Variables
You can use useState multiple times in a component:
function Form() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [age, setAge] = useState(0);
// Each has its own state and setter
}
State vs Props
| Props | State |
|---|---|
| Passed from parent | Managed within component |
| Read-only | Can be changed |
| Component can't change them | Component controls them |
Common Mistakes
1. Mutating State Directly
// ❌ Wrong
user.name = 'Bob';
setUser(user);
// ✅ Correct
setUser({ ...user, name: 'Bob' });
2. Forgetting State is Asynchronous
setCount(count + 1);
console.log(count); // Still shows old value!
// State updates are scheduled, not immediate
3. Using Objects When Separate Values Work
// Consider if these really need to be together
const [formData, setFormData] = useState({ name: '', email: '' });
// vs separate, simpler state
const [name, setName] = useState('');
const [email, setEmail] = useState('');
Key Takeaways
- State is a component's memory that persists across renders
useStatereturns[value, setter]- Never mutate state directly - always use the setter
- Use functional updates when new state depends on old state
- State updates trigger re-renders