Skip to solution
mediumFrontend

Explain the purpose of `useEffect`.

364 views
01

Understand the problem

Question presented to candidate: "What is useEffect for — and, just as importantly, what is it not for?"

What a strong answer should cover:

  • The modern framing: an effect synchronises a component with an external system — the network, a subscription, the DOM, a timer. It is not a general-purpose lifecycle hook.
  • It runs after render and after the browser paints, so it never blocks the visible update.
  • The dependency array decides when it re-runs; the returned cleanup undoes the previous run.
  • The most valuable half of the answer: when not to use one. Derived values belong in render; user-action responses belong in event handlers.
  • Deriving state in an effect costs an extra render pass and leaves a moment where the UI shows the stale value.
  • Effects are an escape hatch from the React paradigm — the React docs categorise them that way deliberately.
  • Common wrong uses: transforming data for display, resetting state on prop change (use key), and doing work that belongs in a submit handler.
  • useLayoutEffect as the pre-paint exception; useEffectEvent for non-reactive logic inside an effect.

Clarifying questions expected:

  • "Is this synchronising with something outside React, or transforming data we already have?" — that single question decides whether an effect belongs at all.

Code / implementation expected: Yes — an effect that genuinely synchronises, beside a derived value that should not be one.

useEffectside effectslifecyclehooks
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for React interviews — assumes useState. Difficulty: Medium

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. The render counts in section 4 were measured on React 19.2.8. This doc focu

Solution ready — 2 min read

Classified // press E to declassify

04

Explore the playground snippets

An effect that genuinely synchronises, beside two that should not exist
Run Playground
import { useState, useEffect, useMemo } from "react";

const ALL = ["apple", "banana", "cherry", "date", "elderberry", "fig"];

// ❌ ANTI-PATTERN: derived state in an effect. Renders once with the stale
// value, then the effect sets state and forces a second render.
function WrongDerived({ query }) {
  const [results, setResults] = useState([]);
  const [renders, setRenders] = useState(0);
  useEffect(() => {
    setResults(ALL.filter((f) => f.includes(query)));
  }, [query]);
  useEffect(() => { setRenders((r) => r + 1); }, [results]);
  return <Line label="via effect (extra pass)" items={results} />;
}

// ✅ Just compute it. One render, never stale.
function RightDerived({ query }) {
  const results = useMemo(() => ALL.filter((f) => f.includes(query)), [query]);
  return <Line label="computed in render" items={results} />;
}

// ✅ A REAL effect: synchronising with something outside React. There is an
// external system (the document title) that must be started, kept matching,
// and restored on teardown.
function TitleSync({ query }) {
  useEffect(() => {
    const previous = document.title;
    document.title = query ? "search: " + query : "no search";
    return () => { document.title = previous; };
  }, [query]);
  return <p style={{ fontSize: 13, color: "#666" }}>document.title is synced to the query</p>;
}

function Line({ label, items }) {
  return (
    <p style={{ margin: "4px 0" }}>
      <code style={{ display: "inline-block", minWidth: 200 }}>{label}</code>
      {items.length ? items.join(", ") : "(none)"}
    </p>
  );
}

// ✅ Resetting state on a prop change WITHOUT an effect: the key prop.
function Editor({ docId }) {
  const [text, setText] = useState("");
  return (
    <div>
      <input
        value={text}
        placeholder={"editing doc " + docId}
        onChange={(e) => setText(e.target.value)}
      />
    </div>
  );
}

export default function App() {
  const [query, setQuery] = useState("a");
  const [docId, setDocId] = useState(1);

  return (
    <div style={{ padding: 24, fontFamily: "system-ui", lineHeight: 1.6 }}>
      <input value={query} onChange={(e) => setQuery(e.target.value)} placeholder="filter" />
      <WrongDerived query={query} />
      <RightDerived query={query} />
      <TitleSync query={query} />

      <hr />
      <h4 style={{ margin: "0 0 8px" }}>Reset state with key, not an effect</h4>
      {/* Changing the key remounts Editor, so its text state resets by itself. */}
      <Editor key={docId} docId={docId} />
      <p>
        <button onClick={() => setDocId((d) => d + 1)}>Open the next document</button>
      </p>
      <p style={{ color: "#666", fontSize: 13 }}>
        Type in the editor, then open the next document — the field clears with
        no effect and no manual reset, because React remounted the component.
      </p>
    </div>
  );
}
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 61 of 119 decoded in the React.js track. One more won't hurt.

Back to track