useState Hook — Managing State in React
DodaTech
3 min read
In this tutorial, you'll learn about usestate hook. We cover key concepts, practical examples, and best practices.
What You'll Learn
Master React's useState hook — declare and update state, work with objects and arrays, handle forms, follow best practices, and avoid common pitfalls.
Why It Matters
State is the heart of React. Every interactive component needs state. Understanding useState deeply prevents bugs and makes your components predictable.
Real-World Use
A form with multiple inputs, a toggle switch, a counter, a shopping cart with items, or any component that changes based on user interaction.
Basic useState
import { useState } from "react";
function Counter() {
// Declare state variable: [value, setter]
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>
);
}
Multiple State Variables
function Form() {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [age, setAge] = useState(0);
const [isSubscribed, setIsSubscribed] = useState(false);
return (
<form>
<input
value={name}
onChange={e => setName(e.target.value)}
placeholder="Name"
/>
<input
value={email}
onChange={e => setEmail(e.target.value)}
placeholder="Email"
/>
<label>
<input
type="checkbox"
checked={isSubscribed}
onChange={e => setIsSubscribed(e.target.checked)}
/>
Subscribe to newsletter
</label>
</form>
);
}
State with Objects
function UserForm() {
const [user, setUser] = useState({
name: "",
email: "",
address: {
street: "",
city: "",
},
});
// ❌ Wrong — mutates state directly
function handleWrongInput(e) {
user.name = e.target.value; // DON'T DO THIS
}
// ✅ Correct — create a new object
function handleNameChange(e) {
setUser({
...user, // Spread existing properties
name: e.target.value,
});
}
// ✅ Nested object update
function handleStreetChange(e) {
setUser({
...user,
address: {
...user.address,
street: e.target.value,
},
});
}
return (
<div>
<input value={user.name} onChange={handleNameChange} />
<input value={user.address.street} onChange={handleStreetChange} />
</div>
);
}
State with Arrays
function TodoList() {
const [todos, setTodos] = useState([]);
// Add
function addTodo(text) {
setTodos([
...todos,
{ id: Date.now(), text, completed: false },
]);
}
// Remove
function removeTodo(id) {
setTodos(todos.filter(todo => todo.id !== id));
}
// Update (toggle complete)
function toggleTodo(id) {
setTodos(todos.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
));
}
// Clear all
function clearAll() {
setTodos([]);
}
return (
<div>
<button onClick={() => addTodo("New task")}>Add</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<span
style={{
textDecoration: todo.completed ? "line-through" : "none",
}}
onClick={() => toggleTodo(todo.id)}
>
{todo.text}
</span>
<button onClick={() => removeTodo(todo.id)}>×</button>
</li>
))}
</ul>
</div>
);
}
Functional Updates
When new state depends on previous state, use a function:
function Counter() {
const [count, setCount] = useState(0);
// ❌ Bug: if this runs multiple times, count might be stale
function handleClick() {
setCount(count + 1); // May use stale value
setCount(count + 1); // This doesn't add 2 — uses same count
}
// ✅ Correct: functional update
function handleClick() {
setCount(prev => prev + 1);
setCount(prev => prev + 1);
}
// Result: count increases by 2
}
Lazy Initial State
If your initial state requires expensive computation:
// ❌ Expensive computation runs on every render
const [data, setData] = useState(expensiveComputation());
// ✅ Runs only once (on initial render)
const [data, setData] = useState(() => expensiveComputation());
Common Mistakes
| Mistake | Fix |
|---|---|
| Mutating state directly | Always use setter function |
| Forgetting spread for objects | setState({...state, key: value}) |
| Stale closures in effects | Use functional updates |
| Too many useState calls | Use useReducer for complex state |
| Derivable state in useState | Compute on the fly instead |
| Not using callback form | setCount(prev => prev + 1) |
Practice
// Build a component with:
// 1. A text input
// 2. A counter with increment/decrement/reset
// 3. A list where you can add and remove items
// 4. A toggle switch
← Previous
React Hooks — Complete Guide (useState, useEffect, useContext, useReducer)
Next →
useEffect Hook — Side Effects in React
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro