AiTechWorlds
AiTechWorlds
University learning companion
React & Next.js Complete Course · useState: State Management
A feature looks correct on a developer’s large monitor but fails on a slow connection and a small screen. Use this topic to inspect structure, state, loading/error behaviour, accessibility and the user journey—not just the happy path.
Use this course-level map to place “useState: State Management” in the larger system. It complements—not replaces—a topic-specific worked diagram.
State is data a component owns and remembers between renders change it, and the component redraws itself automatically. useState is the hook that gives a component this memory.
A thermostat is a good analogy. It doesn't just display the temperature once and freeze it holds a target value in memory, and every time you turn the dial, the display updates and the system reacts. A React component without state is a printed thermometer: accurate once, then frozen forever.
State is data that:
Not state:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // [currentValue, setterFunction]
// ^— initial value
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>
);
}useState returns an array with exactly two items:
React detects state changes by reference equality. If you mutate the current value directly, React won't know it changed.
// WRONG — mutating state
const [user, setUser] = useState({ name: "Alice", age: 30 });
function birthday() {
user.age++; // Mutates the existing object
setUser(user); // React sees the same reference — no re-render!
}
// RIGHT — always create a new value
function birthday() {
setUser({ ...user, age: user.age + 1 }); // New object = new reference
}Same rule for arrays:
// WRONG
const [items, setItems] = useState(["a", "b", "c"]);
items.push("d"); // Mutates — won't trigger re-render
// RIGHT
setItems([...items, "d"]); // Add item
setItems(items.filter(i => i !== "b")); // Remove item
setItems(items.map(i => i === "a" ? "A" : i)); // Update itemWhen new state depends on previous state, use a function:
// This can have race conditions with multiple rapid clicks
setCount(count + 1);
// This is always correct — uses the latest state
setCount(prevCount => prevCount + 1);The function form guarantees you're working with the most recent state value:
function Counter() {
const [count, setCount] = useState(0);
function handleTripleIncrement() {
// These all run before re-render — count is still 0 for all three
setCount(count + 1); // → 1
setCount(count + 1); // → 1 again! count was still 0
setCount(count + 1); // → 1 again!
// Use functional update to correctly chain:
setCount(prev => prev + 1); // → 1
setCount(prev => prev + 1); // → 2
setCount(prev => prev + 1); // → 3
}
}function UserForm() {
const [form, setForm] = useState({
name: '',
email: '',
role: 'user',
});
function handleChange(field, value) {
setForm(prev => ({
...prev, // Keep all existing fields
[field]: value // Update only the changed field
}));
}
return (
<form>
<input
value={form.name}
onChange={e => handleChange('name', e.target.value)}
/>
<input
value={form.email}
onChange={e => handleChange('email', e.target.value)}
/>
<select
value={form.role}
onChange={e => handleChange('role', e.target.value)}
>
<option value="user">User</option>
<option value="admin">Admin</option>
</select>
</form>
);
}function TodoApp() {
const [todos, setTodos] = useState([]);
const [input, setInput] = useState('');
function addTodo() {
if (!input.trim()) return;
setTodos(prev => [
...prev,
{ id: Date.now(), text: input.trim(), completed: false }
]);
setInput('');
}
function toggleTodo(id) {
setTodos(prev =>
prev.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
}
function deleteTodo(id) {
setTodos(prev => prev.filter(todo => todo.id !== id));
}
return (
<div>
<input
value={input}
onChange={e => setInput(e.target.value)}
onKeyDown={e => e.key === 'Enter' && addTodo()}
placeholder="Add a todo..."
/>
<button onClick={addTodo}>Add</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<span
onClick={() => toggleTodo(todo.id)}
style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
>
{todo.text}
</span>
<button onClick={() => deleteTodo(todo.id)}>×</button>
</li>
))}
</ul>
<p>{todos.filter(t => !t.completed).length} remaining</p>
</div>
);
}If the initial state is expensive to compute, pass a function:
// This runs getExpensiveValue() on every render — wasteful
const [data, setData] = useState(getExpensiveValue());
// This only runs once — React calls the function on mount only
const [data, setData] = useState(() => getExpensiveValue());
// Common use: read from localStorage
const [theme, setTheme] = useState(() => {
return localStorage.getItem('theme') ?? 'light';
});// Mistake 1: State too low — should be lifted
// Wrong: both siblings need the count, but only one has it
function Parent() {
return (
<>
<CounterDisplay /> {/* Can't see the count */}
<CounterButton /> {/* Has the count state */}
</>
);
}
// Correct: lift state to parent
function Parent() {
const [count, setCount] = useState(0);
return (
<>
<CounterDisplay count={count} />
<CounterButton onIncrement={() => setCount(c => c + 1)} />
</>
);
}
// Mistake 2: Redundant state (derived data)
// Wrong: don't store what you can calculate
const [firstName, setFirstName] = useState('Alice');
const [lastName, setLastName] = useState('Smith');
const [fullName, setFullName] = useState('Alice Smith'); // Redundant!
// Correct: calculate it
const fullName = `${firstName} ${lastName}`; // No state neededNext lesson: useEffect managing side effects like data fetching, subscriptions, and timers.
University learning companion
React & Next.js Complete Course · useState: State Management
Complete the steps without looking at the answer first; record evidence rather than only a final result.
These are primary or established learning references for this subject. Check the relevant chapter or specification for the exact topic and version.
Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises