Probably you not need useEffect

Tuesday, February 17, 20264 min read

React useEffect is one of the first hooks people learn after useState.
It’s also one of the most misunderstood.

For many teams, useEffect becomes a kind of universal solvent: whenever something feels off in a component, an effect is added until it behaves. The code “works”, the UI updates, and the PR gets merged.

What’s less visible is the cost: extra renders, delayed state convergence, and components that only make sense if you simulate React’s lifecycle in your head.

This post isn’t about banning useEffect. It’s about understanding when its use creates work React never asked you to do.

Core mistake (using effects to derive state)

Consider a simple example.

1function Price({ amount, tax }: { amount: number; tax: number }) {
2 const [total, setTotal] = useState(0);
3
4 useEffect(() => {
5 setTotal(amount + tax);
6 }, [amount, tax]);
7
8 return <span>{total}</span>;
9}

This looks reasonable. It is also inefficient by construction.

What React actually does?

  1. Initial render

    • total is 0
    • React paints 0 to the screen
  2. Effect phase

    • setTotal(amount + tax) runs
  3. Second render

    • total is now correct
    • React paints again

You have forced two renders for a value that is a pure calculation.

If a value can be calculated during render, putting it in useEffect guarantees at least one extra render.

Render time calculation is not an optimization

The fix is boring, which is a compliment.

1function Price({ amount, tax }: { amount: number; tax: number }) {
2 const total = amount + tax;
3 return <span>{total}</span>;
4}

This is not a micro-optimization. It’s aligning with how React is designed to work.

Props → State Syncing (accidental double rendering)

Another common pattern looks like this:

1function UserBadge({ user }: { user: User }) {
2 const [name, setName] = useState("");
3
4 useEffect(() => {
5 setName(user.name);
6 }, [user.name]);
7
8 return <span>{name}</span>;
9}

Every time user.name changes, the component renders twice:

  1. Render with stale state
  2. Effect runs
  3. Render again with updated state

Worse, you’ve now split a single fact (user.name) into two sources of truth.

In small components this is easy to miss. In larger ones, it becomes a breeding ground for bugs that only appear “sometimes”.

The correct solution is usually the simplest one:

1function UserBadge({ user }: { user: User }) {
2 return <span>{user.name}</span>;
3}

Why this scales poorly?

One unnecessary effect might cost you one extra render, that doesn’t sound dramatic...but effects compose.

If you have:

The extra work multiplies, React still renders quickly, but the app starts to feel oddly sluggish:

This is how performance problems sneak in—not with a bang, but with “harmless” patterns repeated everywhere.

When useEffect is actually the right tool?

useEffect exists for a reason. It’s just a narrower reason than many people assume.

It’s appropriate when React needs to coordinate with something outside itself:

1useEffect(() => {
2 const onResize = () => setWidth(window.innerWidth);
3 window.addEventListener("resize", onResize);
4 return () => window.removeEventListener("resize", onResize);
5}, []);

This code cannot run during render. It depends on the external world. That’s exactly what effects are for.

What they are not for is finishing a render that could have been correct in the first place.

Simple heuristic

When you reach for useEffect, ask:

Could this logic run during render without breaking anything?

If your answer depends on timing, ordering, or “waiting for React”, that’s a signal to rethink the component, not to add another dependency to the array.

Conclusion

useEffect is not a default mechanism for making components work. It’s an escape hatch for side effects that React cannot model declaratively.

Using it to derive state or sync props introduces:

React already gives you a deterministic render phase. If you lean into it, many effects simply disappear.

And when they don’t, at least you’ll know why they’re there.


Thursday, June 27, 2024

Introduction of useBem for React

Discover the power of the useBem hook to streamline your CSS class management, learn how to apply the BEM methodology to ensure consistent, readable, and maintainable styling across your front-end projects.


Wednesday, October 4, 2023

SOLID Principles in React

Let's explore how the SOLID principles can be applied to React components using TypeScript, functions, hooks, and interfaces.


Monday, July 24, 2023

Using startTransition in React

React 18 introduced a new hook called `startTransition`, simplifies managing asynchronous UI transitions in your React applications.