Stop Overusing useState in React.
When you first start learning React, useState feels like absolute magic. It’s the ultimate superpower. Need a dropdown menu to open? Throw a state hook at it. Need to capture user input? Create a state hook. Want to calculate the total cost of items in a shopping cart? Naturally, your first instinct is to create yet another state hook.
Before you know it, you open a component file you wrote a week ago, and the top of your function looks like an endless list of state hooks.
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [fullName, setFullName] = useState(''); // Wait... do we need this?You look at that number of declarations and a tiny voice in your head says, “There is no way professional developers write code that looks this messy.”
If you have ever stared at a fat component feeling like you’re drowning in state hooks, don’t worry. You haven’t built a codebase nightmare — you’ve just stumbled into a classic beginner fault. Let’s talk about why we instinctively overuse state, why it quietly kills your app’s performance, and the simple way to clean it all up.
The Syncing Trap: How We End Up in useEffect Hell
The biggest culprit behind state overconsumption is the desire to sync data.
Let’s look at a realistic scenario: you are building a simple product list dashboard. You want to track the array of items, but you also need to display the total number of items and the total price at the bottom of the screen.
As a beginner, you might write code that looks exactly like this:
const [items, setItems] = useState([]);
const [totalItems, setTotalItems] = useState(0);
const [totalPrice, setTotalPrice] = useState(0);
// Syncing the extra states every time items change
useEffect(() => {
setTotalItems(items.length);
const total = items.reduce((sum, item) => sum + item.price, 0);
setTotalPrice(total);
}, [items]);On the surface, this feels incredibly logical. You think: “When the items change, I need to recalculate the totals and save them into state so the UI updates.”
Get Shreyash’s stories in your inbox
Join Medium for free to get updates from this writer.
But behind the scenes, you have accidentally introduced a major performance flaw.
The Double-Render Punishment
Every time you call a state update function, React is forced to re-render the screen. Let’s look at the actual execution timeline of the code above when a user adds a new item to their cart:
- The Trigger: The user adds a product.
setItemsis called with the new array. - Render #1: React runs the component to update the list of items on the UI.
- The Effect Fires: After the screen is drawn, React notices
itemschanged and triggers youruseEffect. - The Second State Trigger: Inside the effect, you call
setTotalItemsandsetTotalPrice. - Render #2: React is forced to stop what it’s doing and re-render the entire component a second time just to update those numbers.
You are forcing React to do double the work for a single user action. In a large scale app, this pattern leads to lagging interfaces and out-of-sync bugs that are a total headache to debug.
The Answer: Derived State
The golden rule of React state management is simple: If a value can be calculated from existing props or state, it should not be kept in state.
Instead of storing it, you calculate it on the go during the render phase. This is called Derived State, and it requires absolutely no hooks, no effects, and zero state synchronization.
Let’s refactor that exact same shopping cart component using regular JavaScript:
const [items, setItems] = useState([]);
// No hooks, no effects. Just plain JavaScript.
const totalItems = items.length;
const totalPrice = items.reduce((sum, item) => sum + item.price, 0);Look at how much cleaner that is. We cut our code down significantly, but more importantly, let’s look at how React processes this now:
- The Trigger: The user adds a product.
setItemsis called. - Render #1: React re-runs the component function from top to bottom.
- Instant Calculation: As the function executes, JavaScript instantly reads the fresh
itemsarray, counts the length, sums up the price, and injects the updated variables straight into the JSX layout.
One user action. One clean render cycle. Perfect synchronization, every single time.
The Lesson Learned
When you are starting out, deleting code feels terrifying. We treat hooks like a safety blanket — if we don’t explicitly put data into a React hook, we worry React will somehow forget about it.
The major landmark in moving from a beginner to an intermediate developer is realizing that React components are just functions that run repeatedly. Clean out those extra hooks, delete those unnecessary
useEffects, and watch your code instantly become leaner and legible.









