Skip to main content

Command Palette

Search for a command to run...

You probably don't need useEffect for that

Most effects in a React codebase are derived state or event handlers in disguise

Updated
2 min read
You probably don't need useEffect for that
I
Full-Stack Developer @ Bug0 | Node.js, Next.js, JavaScript, TypeScript | Ex-Wipro, DXC, Majesco/Mastek | ex-Java, ex-Spring

useEffect is for synchronizing your component with something outside React: the network, the DOM, a subscription. Most of the effects I see in code reviews are doing neither. They are computing values or responding to clicks, and they would be simpler, faster, and less buggy without an effect at all.

You don't need an effect to transform data for rendering

A common pattern:

const [posts, setPosts] = useState([]);
const [visible, setVisible] = useState([]);

useEffect(() => {
  setVisible(posts.filter((p) => !p.archived));
}, [posts]);

This runs an extra render every time posts changes: one to set posts, another to set visible. Just compute it during render:

const [posts, setPosts] = useState([]);
const visible = posts.filter((p) => !p.archived);

If the filter is genuinely expensive, reach for useMemo, not state plus an effect. Derived values should be derived, not stored.

You don't need an effect to handle an event

Another one:

useEffect(() => {
  if (submitted) {
    showToast("Saved");
    setSubmitted(false);
  }
}, [submitted]);

The toast is a reaction to a user action, so put it in the handler where the action happens:

function handleSubmit() {
  save();
  showToast("Saved");
}

The effect version adds a state variable, an extra render, and a flag you have to reset. The handler version is three lines and obvious.

When an effect is the right tool

Use it when you are reaching outside React and need to clean up after yourself:

useEffect(() => {
  const socket = connect(roomId);
  socket.on("message", onMessage);
  return () => socket.close();
}, [roomId]);

This is a real synchronization: a live connection that must open when roomId changes and close when the component unmounts. The cleanup function is the tell. If your effect has nothing to clean up and only sets state, that is a strong hint it should not be an effect.

A quick checklist

Before writing useEffect, ask:

  1. Can I compute this during render instead? Then do that.
  2. Is this responding to a specific user action? Move it to the handler.
  3. Am I syncing with something external that needs cleanup? Then an effect is correct.

Effects are an escape hatch from React, not a place to put logic that React can already handle.

Fewer effects means fewer renders, fewer race conditions, and code that is far easier to follow.