useState Hook
Master the useState hook for managing component state. This is a foundational concept in component-based UI development that professional developers rely on daily. The explanations below are written to be beginner-friendly while covering the depth and nuance that comes from real-world React experience. Take your time with each section and practice the examples
What is useState?
The useState hook is a React Hook that lets you add state to function components. It returns an array with the current state value and a function to update it.. This is an essential concept that every React developer must understand thoroughly. In professional development environments, getting this right can mean the difference between code that works reliably and code that breaks in production. The following sections break this down into clear, digestible pieces with practical examples you can try immediately
Basic useState Usage
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
<button onClick={() => setCount(count - 1)}>
Decrement
</button>
</div>
);
}
// Multiple state variables
function UserProfile() {
const [name, setName] = useState('');
const [age, setAge] = useState(0);
const [email, setEmail] = useState('');
return (
<div>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
/>
<input
value={age}
onChange={(e) => setAge(Number(e.target.value))}
placeholder="Age"
type="number"
/>
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
</div>
);
}Object State
// Managing object state
function UserForm() {
const [user, setUser] = useState({
name: '',
email: '',
age: 0
});
const updateUser = (field, value) => {
setUser(prevUser => ({
...prevUser,
[field]: value
}));
};
return (
<div>
<input
value={user.name}
onChange={(e) => updateUser('name', e.target.value)}
placeholder="Name"
/>
<input
value={user.email}
onChange={(e) => updateUser('email', e.target.value)}
placeholder="Email"
/>
<input
value={user.age}
onChange={(e) => updateUser('age', Number(e.target.value))}
placeholder="Age"
type="number"
/>
</div>
);
}Array State Management
// Managing arrays with useState
function TodoList() {
const [todos, setTodos] = useState([]);
const [newTodo, setNewTodo] = useState('');
// Add new todo
const addTodo = () => {
if (newTodo.trim()) {
setTodos(prevTodos => [...prevTodos, {
id: Date.now(),
text: newTodo,
completed: false
}]);
setNewTodo('');
}
};
// Toggle todo completion
const toggleTodo = (id) => {
setTodos(prevTodos => prevTodos.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
));
};
// Delete todo
const deleteTodo = (id) => {
setTodos(prevTodos => prevTodos.filter(todo => todo.id !== id));
};
// Update todo text
const updateTodo = (id, newText) => {
setTodos(prevTodos => prevTodos.map(todo =>
todo.id === id
? { ...todo, text: newText }
: todo
));
};
return (
<div>
<input
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
placeholder="Add new todo"
/>
<button onClick={addTodo}>Add</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<input
value={todo.text}
onChange={(e) => updateTodo(todo.id, e.target.value)}
style={{
textDecoration: todo.completed ? 'line-through' : 'none'
}}
/>
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
</div>
);
}
// Complex state with nested objects and arrays
function ShoppingCart() {
const [cart, setCart] = useState({
items: [],
total: 0,
discount: 0,
shipping: 0
});
const addItem = (product) => {
setCart(prevCart => {
const existingItem = prevCart.items.find(item => item.id === product.id);
if (existingItem) {
// Update quantity of existing item
const updatedItems = prevCart.items.map(item =>
item.id === product.id
? { ...item, quantity: item.quantity + 1 }
: item
);
return {
...prevCart,
items: updatedItems,
total: calculateTotal(updatedItems, prevCart.discount, prevCart.shipping)
};
} else {
// Add new item
const newItems = [...prevCart.items, { ...product, quantity: 1 }];
return {
...prevCart,
items: newItems,
total: calculateTotal(newItems, prevCart.discount, prevCart.shipping)
};
}
});
};
const updateQuantity = (productId, quantity) => {
setCart(prevCart => {
const updatedItems = prevCart.items.map(item =>
item.id === productId
? { ...item, quantity: Math.max(0, quantity) }
: item
).filter(item => item.quantity > 0); // Remove items with 0 quantity
return {
...prevCart,
items: updatedItems,
total: calculateTotal(updatedItems, prevCart.discount, prevCart.shipping)
};
});
};
const applyDiscount = (discountPercent) => {
setCart(prevCart => ({
...prevCart,
discount: discountPercent,
total: calculateTotal(prevCart.items, discountPercent, prevCart.shipping)
}));
};
const calculateTotal = (items, discount, shipping) => {
const subtotal = items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
const discountAmount = (subtotal * discount) / 100;
return subtotal - discountAmount + shipping;
};
return (
<div>
<h2>Shopping Cart</h2>
{cart.items.map(item => (
<div key={item.id}>
<span>{item.name}</span>
<span>{item.price}</span>
<input
type="number"
value={item.quantity}
onChange={(e) => updateQuantity(item.id, parseInt(e.target.value))}
min="0"
/>
</div>
))}
<div>
<strong>Total: {cart.total.toFixed(2)}</strong>
</div>
<button onClick={() => applyDiscount(10)}>Apply 10% Discount</button>
</div>
);
}Practice Exercise: Advanced State Management
// Exercise: Build a Task Management System
// Create a comprehensive task management system with multiple state patterns
// src/components/TaskManager.js
import React, { useState } from 'react';
function TaskManager() {
const [tasks, setTasks] = useState([]);
const [categories, setCategories] = useState([
{ id: 1, name: 'Work', color: '#ff6b6b' },
{ id: 2, name: 'Personal', color: '#4ecdc4' },
{ id: 3, name: 'Shopping', color: '#45b7d1' }
]);
const [filters, setFilters] = useState({
category: 'all',
status: 'all',
priority: 'all'
});
const [newTask, setNewTask] = useState({
title: '',
description: '',
categoryId: 1,
priority: 'medium',
dueDate: ''
});
// Add new task
const addTask = () => {
if (newTask.title.trim()) {
const task = {
id: Date.now(),
...newTask,
status: 'pending',
createdAt: new Date().toISOString(),
completedAt: null
};
setTasks(prevTasks => [...prevTasks, task]);
setNewTask({
title: '',
description: '',
categoryId: 1,
priority: 'medium',
dueDate: ''
});
}
};
// Update task status
const updateTaskStatus = (taskId, status) => {
setTasks(prevTasks => prevTasks.map(task =>
task.id === taskId
? {
...task,
status,
completedAt: status === 'completed' ? new Date().toISOString() : null
}
: task
));
};
// Delete task
const deleteTask = (taskId) => {
setTasks(prevTasks => prevTasks.filter(task => task.id !== taskId));
};
// Filter tasks
const getFilteredTasks = () => {
return tasks.filter(task => {
const categoryMatch = filters.category === 'all' || task.categoryId === parseInt(filters.category);
const statusMatch = filters.status === 'all' || task.status === filters.status;
const priorityMatch = filters.priority === 'all' || task.priority === filters.priority;
return categoryMatch && statusMatch && priorityMatch;
});
};
// Get category by ID
const getCategoryById = (categoryId) => {
return categories.find(cat => cat.id === categoryId);
};
// Get priority color
const getPriorityColor = (priority) => {
const colors = {
low: '#28a745',
medium: '#ffc107',
high: '#dc3545'
};
return colors[priority] || '#6c757d';
};
const filteredTasks = getFilteredTasks();
return (
<div className="task-manager">
{/* Add Task Form */}
<div className="add-task-form">
<h3>Add New Task</h3>
<input
value={newTask.title}
onChange={(e) => setNewTask(prev => ({ ...prev, title: e.target.value }))}
placeholder="Task title"
/>
<textarea
value={newTask.description}
onChange={(e) => setNewTask(prev => ({ ...prev, description: e.target.value }))}
placeholder="Task description"
/>
<select
value={newTask.categoryId}
onChange={(e) => setNewTask(prev => ({ ...prev, categoryId: parseInt(e.target.value) }))}
>
{categories.map(cat => (
<option key={cat.id} value={cat.id}>{cat.name}</option>
))}
</select>
<select
value={newTask.priority}
onChange={(e) => setNewTask(prev => ({ ...prev, priority: e.target.value }))}
>
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
</select>
<input
type="date"
value={newTask.dueDate}
onChange={(e) => setNewTask(prev => ({ ...prev, dueDate: e.target.value }))}
/>
<button onClick={addTask}>Add Task</button>
</div>
{/* Filters */}
<div className="filters">
<select
value={filters.category}
onChange={(e) => setFilters(prev => ({ ...prev, category: e.target.value }))}
>
<option value="all">All Categories</option>
{categories.map(cat => (
<option key={cat.id} value={cat.id}>{cat.name}</option>
))}
</select>
<select
value={filters.status}
onChange={(e) => setFilters(prev => ({ ...prev, status: e.target.value }))}
>
<option value="all">All Status</option>
<option value="pending">Pending</option>
<option value="in-progress">In Progress</option>
<option value="completed">Completed</option>
</select>
<select
value={filters.priority}
onChange={(e) => setFilters(prev => ({ ...prev, priority: e.target.value }))}
>
<option value="all">All Priorities</option>
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
</select>
</div>
{/* Task List */}
<div className="task-list">
<h3>Tasks ({filteredTasks.length})</h3>
{filteredTasks.map(task => {
const category = getCategoryById(task.categoryId);
return (
<div key={task.id} className="task-item">
<div className="task-header">
<h4>{task.title}</h4>
<span
className="priority-badge"
style={{ backgroundColor: getPriorityColor(task.priority) }}
>
{task.priority}
</span>
<span
className="category-badge"
style={{ backgroundColor: category.color }}
>
{category.name}
</span>
</div>
<p>{task.description}</p>
<div className="task-meta">
<span>Due: {task.dueDate}</span>
<span>Status: {task.status}</span>
</div>
<div className="task-actions">
<select
value={task.status}
onChange={(e) => updateTaskStatus(task.id, e.target.value)}
>
<option value="pending">Pending</option>
<option value="in-progress">In Progress</option>
<option value="completed">Completed</option>
</select>
<button onClick={() => deleteTask(task.id)}>Delete</button>
</div>
</div>
);
})}
</div>
</div>
);
}
// Challenge: Add task editing functionality
// Challenge: Add task search functionality
// Challenge: Add task statistics and analytics
// Challenge: Add task export/import functionality