Building Your Learning Module...
Getting things ready for you!
Find videos you like?
Save to resource drawer for future reference!
In JSX components, state variables don't update immediately. Each render gets a snapshot of the state. When you call setState multiple times, you might be working with outdated values.
All three calls use the same snapshot where count = 0.
Each function gets the latest queued value.
An updater function is a function you pass to setState that receives the previous state and returns the new state. This is essential for JSX components that need multiple state updates.
Function added to update queue
Current state (0) passed to function
โ Component re-renders with new state
Replaces state with this value immediately
Queues function to process with latest state
Click both buttons to see how they behave differently
function CounterComparison() {
const [count, setCount] = React.useState(0);
const [clicks, setClicks] = React.useState(0);
const handleBadClick = () => {
setClicks(clicks + 1);
// โ All use the same snapshot
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
};
const handleGoodClick = () => {
setClicks(clicks + 1);
// โ
Each uses the latest queue value
setCount(c => c + 1);
setCount(c => c + 1);
setCount(c => c + 1);
};
const reset = () => {
setCount(0);
setClicks(0);
};
return (
<div className="container">
<h1>๐ข State Update Comparison</h1>
<div className="stats-grid">
<div className="stat-card">
<div className="stat-label">Current Count</div>
<div className="stat-value">{count}</div>
</div>
<div className="stat-card">
<div className="stat-label">Total Clicks</div>
<div className="stat-value">{clicks}</div>
</div>
</div>
<div className="button-grid">
<button
className="btn-bad"
onClick={handleBadClick}
>
โ +3 (Wrong Way)
<span>Uses snapshot value</span>
</button>
<button
className="btn-good"
onClick={handleGoodClick}
>
โ
+3 (Right Way)
<span>Uses queue value</span>
</button>
<button
className="btn-reset"
onClick={reset}
>
๐ Reset
</button>
</div>
<div className="code-explanation">
<div className="section bad">
<h3>โ Regular Updates (Snapshot Problem)</h3>
<div className="code-block">
<div>setCount({count} + 1); // = {count + 1}</div>
<div>setCount({count} + 1); // = {count + 1}</div>
<div>setCount({count} + 1); // = {count + 1}</div>
</div>
<p>All three calls use the same count value: {count}</p>
<div className="result-box">
Result: {count} โ {count + 1}
</div>
</div>
<div className="section good">
<h3>โ
Updater Functions (Queue Solution)</h3>
<div className="code-block">
<div>setCount(c => {count} + 1); // = {count + 1}</div>
<div>setCount(c => {count + 1} + 1); // = {count + 2}</div>
<div>setCount(c => {count + 2} + 1); // = {count + 3}</div>
</div>
<p>Each call gets the latest queued value</p>
<div className="result-box">
Result: {count} โ {count + 3}
</div>
</div>
</div>
</div>
);
}
const root = createRoot(document.getElementById('root'));
root.render(<CounterComparison />);Loading preview...
Add, remove, and toggle todos using proper state update patterns
function TodoManager() {
const [todos, setTodos] = React.useState([
{ id: 1, text: 'Learn React state updates', done: false },
{ id: 2, text: 'Build awesome components', done: true }
]);
const [newTodo, setNewTodo] = React.useState('');
const [filter, setFilter] = React.useState('all');
const addTodo = () => {
if (newTodo.trim()) {
setTodos(t => [...t, {
id: Date.now(),
text: newTodo.trim(),
done: false
}]);
setNewTodo('');
}
};
const toggleTodo = (id) => {
setTodos(todos => todos.map(todo =>
todo.id === id ? { ...todo, done: !todo.done } : todo
));
};
const deleteTodo = (id) => {
setTodos(todos => todos.filter(todo => todo.id !== id));
};
const clearCompleted = () => {
setTodos(todos => todos.filter(todo => !todo.done));
};
const toggleAll = () => {
setTodos(todos => {
const allDone = todos.every(todo => todo.done);
return todos.map(todo => ({ ...todo, done: !allDone }));
});
};
const filteredTodos = todos.filter(todo => {
if (filter === 'active') return !todo.done;
if (filter === 'completed') return todo.done;
return true;
});
const stats = {
total: todos.length,
active: todos.filter(t => !t.done).length,
completed: todos.filter(t => t.done).length
};
return (
<div className="container">
<h1>๐ Todo List Manager</h1>
<div className="stats-bar">
<div className="stat">
<span className="stat-number">{stats.total}</span>
<span className="stat-label">Total</span>
</div>
<div className="stat">
<span className="stat-number">{stats.active}</span>
<span className="stat-label">Active</span>
</div>
<div className="stat">
<span className="stat-number">{stats.completed}</span>
<span className="stat-label">Done</span>
</div>
</div>
<div className="add-todo">
<input
type="text"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && addTodo()}
placeholder="What needs to be done?"
className="todo-input"
/>
<button onClick={addTodo} className="add-btn">
โ Add
</button>
</div>
<div className="filter-tabs">
{['all', 'active', 'completed'].map(f => (
<button
key={f}
className={filter === f ? 'active' : ''}
onClick={() => setFilter(f)}
>
{f.charAt(0).toUpperCase() + f.slice(1)}
</button>
))}
</div>
<div className="todo-list">
{filteredTodos.length === 0 ? (
<div className="empty-state">
{filter === 'completed' ? '๐ No completed tasks!' :
filter === 'active' ? '๐ No active tasks!' :
'๐ Add your first todo!'}
</div>
) : (
filteredTodos.map(todo => (
<div key={todo.id} className={'todo-item ' + (todo.done ? 'done' : '')}>
<input
type="checkbox"
checked={todo.done}
onChange={() => toggleTodo(todo.id)}
className="todo-checkbox"
/>
<span className="todo-text">{todo.text}</span>
<button
onClick={() => deleteTodo(todo.id)}
className="delete-btn"
>
๐๏ธ
</button>
</div>
))
)}
</div>
<div className="actions">
<button onClick={toggleAll} className="action-btn">
{todos.every(t => t.done) ? 'โฌ Uncheck All' : 'โ๏ธ Check All'}
</button>
<button
onClick={clearCompleted}
className="action-btn"
disabled={stats.completed === 0}
>
๐งน Clear Completed
</button>
</div>
<div className="state-display">
<h3>๐ Current State</h3>
<pre>{JSON.stringify({ todos, filter, newTodo }, null, 2)}</pre>
</div>
</div>
);
}
const root = createRoot(document.getElementById('root'));
root.render(<TodoManager />);Loading preview...
When building JSX components, following these patterns will make your state management more predictable and your components easier to debug.
Always use updater functions when new state depends on previous state.
Combine related state into a single object for easier management.
Keep state updates pure. Handle side effects in useEffect.
Use meaningful variable names in your updater functions.
Use updater function to toggle based on current state
Can cause issues with rapid clicks or batching
Always create new arrays using spread operator or array methods
Use spread operator to copy existing properties and update specific ones
Each render gets a snapshot of state. Multiple setState calls may use outdated values.
Pass functions to setState when new state depends on previous state.
State updates are queued and processed in order during the next render.
Keep state updates pure and handle side effects in useEffect hooks.