Stop Treating useEffect Like componentDidMount

The purpose of an effect

An effect synchronizes your rendered UI with something React does not own: a network connection, a browser API, a third-party widget, or a subscription. It is not the place to calculate values from props and state, respond to every render by default, or imitate class lifecycle names.

When you adopt this definition, many effects disappear. Less effect code means fewer dependency puzzles and fewer intermediate renders.

Derived values belong in render

If fullName comes from firstName and lastName, calculate it during render. If a list needs filtering, calculate the filtered list during render. Writing these values into state through an effect adds a render where the UI is temporarily out of sync.

// No effect required
const visibleTodos = todos.filter(todo =>
  todo.title.toLowerCase().includes(query.toLowerCase())
);

Dependencies are a description, not a tuning knob

Every reactive value read by an effect must be declared as a dependency. Leaving one out says the effect does not care if it changes, which is often a stale closure bug. Adding an unstable object or function can make an effect rerun, which is a signal to reconsider where that value is created or whether an effect is necessary.

Move event-specific work into the event handler. Put fetch functions inside an effect when the effect owns the fetch. Do not reach for memoization before understanding the data flow.

Cleanup is the other half of setup

When an effect subscribes, starts a timer, connects a socket, or issues cancellable work, it must clean up before React runs a replacement effect and when the component unmounts. Development Strict Mode deliberately exposes effects that fail this test by exercising setup and cleanup more aggressively.

useEffect(() => {
  const socket = connect(roomId);
  socket.on("message", onMessage);

  return () => {
    socket.off("message", onMessage);
    socket.close();
  };
}, [roomId]);

Key Takeaways

  • Effects synchronize external systems; render derives UI.
  • A dependency list documents every reactive value an effect reads.
  • Every setup with a lifetime needs a cleanup with the matching lifetime.

Ready to explain this under interview pressure?

Learn the mental models, then practise applying them to realistic frontend problems.

Join Cohort 3 Waitlist
Back to Articles