Sitemap
Stackademic

Stackademic is a learning hub for programmers, devs, coders, and engineers. Our goal is to democratize free coding education for the world.

Stop Overusing useState in React.

4 min readJul 10, 2026

--

Press enter or click to view image in full size

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. setItems is 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 items changed and triggers your useEffect.
  • The Second State Trigger: Inside the effect, you call setTotalItems and setTotalPrice.
  • 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. setItems is called.
  • Render #1: React re-runs the component function from top to bottom.
  • Instant Calculation: As the function executes, JavaScript instantly reads the fresh items array, 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.

--

Shreyash
Shreyash

Written by Shreyash

CS Student at BIT Mesra | Deep diving into the JavaScript ecosystem. Sharing my daily tech-learning journey.

Stackademic
Stackademic

Published in Stackademic

Stackademic is a learning hub for programmers, devs, coders, and engineers. Our goal is to democratize free coding education for the world.