Use Cases of useState in React a Beginner should avoid
The useState
hook is one of the most commonly used hooks in React, but many developers make subtle mistakes that lead to performance issues, unnecessary re-renders, or even incorrect state updates. Let’s explore the best and worst use cases of useState
, with real-world mistakes and their solutions.
1. Updating State Directly Instead of Using setState
❌ The Mistake:
Some developers (also Me😅) try to update the state directly like this:
const [user, setUser] = useState({ name: "John", age: 30 });
user.age = 31; // ❌ Bad practice
console.log(user.age); // 31, but component won't re-render
⚠️ Why is this wrong?
- React doesn’t detect direct modifications to objects or arrays.
- The component will not re-render with the new state.
✅ The Fix:
Always create a new object reference when updating state:
setUser(prevUser => ({ ...prevUser, age: 31 })); // ✅ Correct way
Why? React will detect the change and trigger a re-render.
2. Forgetting Functional Updates When State Depends on Previous State
❌ The Mistake:
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1); // ❌ Might not work as expected
};
⚠️ Why is this wrong?
- If multiple state updates happen quickly, React may batch updates and use stale state. Here log value gives old one because log sometimes don't detect sudden state change.
✅ The Fix:
Use functional updates to get the latest state:
const increment = () => {
setCount(prevCount => prevCount + 1); // ✅ Ensures correct updates
};
Why? It guarantees that the latest state value is used.
3. Using useState
for Complex or Nested State
❌ The Mistake:
Using useState
for deeply nested objects:
const [user, setUser] = useState({
name: "John",
address: { city: "New York", zip: "10001" },
});
setUser({ ...user, address: { zip: "10002" } }); // ❌ Overwrites the city property
⚠️ Why is this wrong?
- When updating
address
, thecity
value is lost.
✅ The Fix:
Always spread and preserve nested properties:
setUser(prevUser => ({
...prevUser,
address: { ...prevUser.address, zip: "10002" }
})); // ✅ Preserves all properties
Better alternative? Use useReducer
for complex state logic.
4. Storing Computed Values in useState
Instead of Computing on Render
❌ The Mistake:
const [filteredList, setFilteredList] = useState(items.filter(item => item.active));
⚠️ Why is this wrong?
- The state becomes stale if
items
change. - You don’t need to store derived values in state.
✅ The Fix:
Use useMemo
instead:
const filteredList = useMemo(() => items.filter(item => item.active), [items]);
Why? This recalculates the value only when items
change.
5. Reinitializing State on Every Render
❌ The Mistake:
const [count, setCount] = useState(Math.random()); // ❌ Changes on every render
⚠️ Why is this wrong?
Math.random()
runs on every render, resetting state unnecessarily.
✅ The Fix:
Use a lazy initializer:
const [count, setCount] = useState(() => Math.random()); // ✅ Runs only on mount
Why? It ensures that the initial value is set only once.
6. Using useState
for Frequently Changing Values (Like Window Size)
❌ The Mistake:
const [width, setWidth] = useState(window.innerWidth);
window.addEventListener("resize", () => setWidth(window.innerWidth)); // ❌ Causes performance issues
⚠️ Why is this wrong?
- Every resize triggers state updates, causing unnecessary re-renders.
✅ The Fix:
Use useRef
or useEffect
with debouncing instead:
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []); // ✅ Efficient event handling
Why? It prevents excessive re-renders.
Final Thoughts
The useState
hook is powerful but easy to misuse. By following these best practices, you can avoid common pitfalls, improve performance, and write cleaner React code.
Got more mistakes you’ve encountered? Share them in the comments!
0 Comments