Skip to the solution
HardFrontend6 min read

What are Effect Events (useEffectEvent) and what problem do they solve?

994 views

The problem

Question presented to candidate: "An effect connects to a chat room and shows a notification using the current theme. Changing the theme should not reconnect. How do you express that?"

What a strong answer should cover:

  • The problem: some values an effect uses are not values it should react to. The dependency array has only one setting for both.
  • useEffectEvent splits them: code inside an Effect Event always sees the latest props and state, but the event itself is not a dependency.
  • So the effect re-runs only for genuinely reactive values, while still reading current ones.
  • The alternatives it replaces: lying to the lint rule (stale closure), or a ref updated every render (verbose, easy to misuse).
  • Constraints: only call an Effect Event from inside an effect in the same component β€” never pass it to another component or call it during render.
  • It is stable on React 19.2.8, exported from react (not experimental_useEffectEvent).
  • The distinction to state: props/state you should re-synchronise on are reactive; things you merely want to read at the moment something happens are non-reactive.

Clarifying questions expected:

  • "Which of these values should cause a reconnect, and which are just read at connect time?" β€” that question is the answer.

Code / implementation expected: Yes β€” the chat-room example with a reactive room and a non-reactive theme.

  • react
  • hooks
  • effects

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

6 min read Β· 9 sections

Try it in the playground

Open any example in the playground to run and change it.

ReactA chat room that reconnects on the room but not on the theme
import { useState, useEffect, useEffectEvent } from "react";

// A fake chat service so the connections are observable.
function connect(roomId, log) {
  log("connect: " + roomId);
  return { disconnect: () => log("disconnect: " + roomId) };
}

function ChatRoom({ roomId, theme, log }) {
  // The Effect Event always sees the LATEST theme, but is not a dependency,
  // so changing the theme cannot cause a reconnect.
  const onConnected = useEffectEvent(() => {
    log("notify (theme=" + theme + "): joined " + roomId);
  });

  useEffect(() => {
    const conn = connect(roomId, log);
    onConnected();
    return () => conn.disconnect();
    // roomId only. The lint rule accepts this β€” onConnected is not reactive.
  }, [roomId]);

  return (
    <p style={{
      margin: "8px 0", padding: 8, borderRadius: 6,
      background: theme === "dark" ? "#222" : "#eee",
      color: theme === "dark" ? "#eee" : "#222",
    }}>
      In <strong>{roomId}</strong>, theme <strong>{theme}</strong>
    </p>
  );
}

const ROOMS = ["general", "random", "support"];

export default function App() {
  const [roomId, setRoomId] = useState("general");
  const [theme, setTheme] = useState("dark");
  const [log, setLog] = useState([]);
  const push = (line) => setLog((l) => [...l, line]);

  const connects = log.filter((l) => l.startsWith("connect")).length;

  return (
    <div style={{ padding: 24, fontFamily: "system-ui", lineHeight: 1.6 }}>
      <ChatRoom roomId={roomId} theme={theme} log={push} />

      <p>
        {ROOMS.map((r) => (
          <button key={r} onClick={() => setRoomId(r)} disabled={r === roomId} style={{ marginRight: 6 }}>
            {r}
          </button>
        ))}
        <button onClick={() => setTheme((t) => (t === "dark" ? "light" : "dark"))}>
          toggle theme
        </button>{" "}
        <button onClick={() => setLog([])}>clear</button>
      </p>

      <p style={{ fontSize: 14 }}>connections made: <strong>{connects}</strong></p>

      <pre style={{ background: "#f6f6f6", padding: 10, borderRadius: 6, fontSize: 12, maxHeight: 170, overflow: "auto" }}>
        {log.length ? log.join("\n") : "(nothing yet)"}
      </pre>

      <p style={{ color: "#666", fontSize: 13 }}>
        Toggle the theme repeatedly: the connection count does not move, but the
        panel restyles. Now switch rooms β€” one reconnect, and the notification
        reports the theme you currently have, not the one you started with.
      </p>
    </div>
  );
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 75 of 119 in React.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All React.js questions
Up nextHard

What does the `useActionState` hook do in React 19?

Continue