π₯ Stop Using useState for Everything in React β The Hidden Performance Trap No One Talks About!
If youβve been developing in React for a while, youβve probably used useState a lot... maybe too much. While useState is a powerful and fundamental React hook, overusing it β or using it for the wrong purposes β can actually degrade your app's performance and cause unnecessary re-renders.
In this deep dive, weβll explore better alternatives, expose hidden pitfalls of useState, and walk through code examples that prove why replacing it in the right scenarios is not only possible, but highly recommended.
β οΈ TL;DR: Stop using useState for global/scoped state that never needs component re-renders. Discover useRef, useReducer, and external state managers.
π¨ Problem: Reactivity vs. Responsibility
React is reactive. When a componentβs state changes, it re-renders. This is by design. But what if you have state that doesnβt need to trigger re-renders? Like:
- Tracking animation steps
- Caching timers, intervals, or DOM elements
- Logging values
- Immutable values
If youβre storing data in useState, React assumes:
βOh, they probably want to re-render this component when the value changes.β
This leads to unintentional waste. π΅βπ«
Better Alternatives: The Trio You Should Be Using
1. useRef: Your Secret Cache πΎ
When you want to store a value without causing re-renders, useRef is gold.
β Bad: Using useState
const [timerId, setTimerId] = useState(null);
useEffect(() => {
const id = setInterval(() => console.log('tick'), 1000);
setTimerId(id);
return () => clearInterval(id);
}, []);
π΄ Every setTimerId call triggers a re-render that brings no UI benefit.
β Good: Using useRef
const timerIdRef = useRef();
useEffect(() => {
timerIdRef.current = setInterval(() => console.log('tick'), 1000);
return () => clearInterval(timerIdRef.current);
}, []);
β Cleaner. No unnecessary re-renders. Use useRef for persistent, mutable data that doesnβt need to trigger views.
2. useReducer: Scalable State Management βοΈ
If your component state is growing in complexity, stop juggling multiple useStates.
β Messy State Explosion
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [email, setEmail] = useState('');
β Centralize with useReducer
const initialState = { firstName: '', lastName: '', email: '' };
function reducer(state, action) {
return {
...state,
[action.field]: action.value,
};
}
const [state, dispatch] = useReducer(reducer, initialState);
value={state.firstName}
onChange={e => dispatch({ field: 'firstName', value: e.target.value })} />
π§ useReducer allows predictability and better debugging especially as your form or logic grows.
3. External State (Zustand, Jotai, Recoil) π
For global or cross-component state, like user auth or theme status:
β Use external lightweight stores like Zustand:
npm install zustand
// store.js
import create from 'zustand';
const useStore = create(set => ({
theme: 'light',
toggleTheme: () => set(state => ({ theme: state.theme === 'light' ? 'dark' : 'light' }))
}));
// App.js
const theme = useStore(state => state.theme);
const toggleTheme = useStore(state => state.toggleTheme);
Zustand doesn't rely on Reactβs render cycle. It offers selective reactivity + blazing-fast updates.
Bonus Tip: Memoization Magic πͺ
Avoid passing fresh function or object references every render unless theyβre memoized.
// Wrong - triggers child re-render
doSomething()} />
// Right
const memoizedHandler = useCallback(() => doSomething(), []);
Final Verdict: Know the Tools, Beat the Re-Renders π§
React is not about forcibly using useState and useEffect everywhere. We have better tools now.
Case | Better Alternative |
---|---|
Persisting mutable data | useRef |
Complex or grouped state updates | useReducer |
Shared/global state | Zustand, Jotai, Redux |
One-off logic or flags | useRef / local vars |
π TL;DR Checklist β Stop Overusing useState
- β Need to persist but not re-render? Use useRef
- β Logical groups of state? Use useReducer
- β Global/shared state? Use external stores
- β Avoid useState for tracking things like timeouts, indexes, canvas, etc.
Level-up your React apps π by using the right state tool for the job.
π§΅ Don't forget to share this article with that one dev who still uses 15 useStates in a form! π
What's Next?
Want a follow-up article with practical migration steps from useState to useReducer or Zustand? Drop a comment below or DM me on X (@reactDoctor)!
π If you need help building efficient user interfaces or state-driven apps β we offer frontend development services.
Top comments (0)