Skip to solution
hardFrontend

What does the `use()` hook do, and why is it special?

528 views
01

Understand the problem

Question presented to candidate: "use() is called a hook but it does not follow the Rules of Hooks. What is it and why is it different?"

What a strong answer should cover:

  • use() reads a resource — a promise or a context — during render. With a promise it suspends until the promise settles; with a context it does what useContext does.
  • It is the only hook that may be called conditionally, in a loop, or inside an early return. React documents this explicitly.
  • Why it can: it does not hold state on the fiber. Ordinary hooks are matched by position in a linked list, so a conditional call shifts every later hook onto the wrong record. use() reads something that already exists, so there is no slot to misalign.
  • With a promise, the promise must be created outside render — a promise created during render is a new one every attempt and never settles.
  • A rejected promise read with use() throws during render, so an error boundary catches it. Pair Suspense (pending) with an error boundary (failed).
  • It is what makes Server Components able to pass a promise to a Client Component and have it awaited during render.
  • It is not a data-fetching library — no caching, no deduplication, no request lifecycle. It only unwraps.

Clarifying questions expected:

  • "Where is the promise created?" — that is the correctness question.
  • "Is there an error boundary above this, as well as a Suspense boundary?"

Code / implementation expected: Optional. Showing a conditional use() next to a conditional useContext makes the point immediately.

reactsuspensedata-fetching
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 senior React interviews — assumes hooks and Suspense. Difficulty: Hard

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. The conditional-call comparison in section 3 was **executed against React

Solution ready — 2 min read

Classified // press E to declassify

04

Explore the playground snippets

use() called conditionally, reading both a context and a promise
Run Playground
import { useState, useContext, use, createContext, Suspense, Component } from "react";

const Theme = createContext("light");

// Created OUTSIDE render. A promise created in the component body would be a
// new promise on every render attempt, and would never settle.
const messageP = new Promise((r) => setTimeout(() => r("resolved value"), 1200));
const failingP = new Promise((_, reject) =>
  setTimeout(() => reject(new Error("the request failed")), 800));
failingP.catch(() => {});                    // keep the console quiet

class Boundary extends Component {
  state = { error: null };
  static getDerivedStateFromError(error) { return { error }; }
  render() {
    if (this.state.error) {
      return <Box tone="bad">error boundary caught: {this.state.error.message}</Box>;
    }
    return this.props.children;
  }
}

// ✅ use() inside a condition, with a hook AFTER it — the arrangement that
//    would corrupt an ordinary hook's slot. React allows exactly this.
function Conditional({ on }) {
  const [before] = useState("before");
  let theme = "(not read)";
  if (on) theme = use(Theme);
  const [after] = useState("after");
  return <Box>{before} · theme={theme} · {after}</Box>;
}

// ❌ The same shape with useContext. Toggling this logs
//    "React has detected a change in the order of Hooks" to the console.
function ConditionalBad({ on }) {
  const [before] = useState("before");
  let theme = "(not read)";
  if (on) theme = useContext(Theme);
  const [after] = useState("after");
  return <Box tone="warn">{before} · theme={theme} · {after}</Box>;
}

function Message() { return <Box tone="good">use(promise) → {use(messageP)}</Box>; }
function Failing() { return <Box>{use(failingP)}</Box>; }

function Box({ children, tone }) {
  const bg = tone === "bad" ? "#fdf0f0" : tone === "good" ? "#f2f9f2" : tone === "warn" ? "#fff8e6" : "#f6f6f8";
  return (
    <div style={{ background: bg, border: "1px solid #ddd", borderRadius: 6, padding: 10, fontSize: 13, marginBottom: 8 }}>
      {children}
    </div>
  );
}

export default function App() {
  const [on, setOn] = useState(false);

  return (
    <Theme.Provider value="dark">
      <div style={{ padding: 24, fontFamily: "system-ui", lineHeight: 1.6, maxWidth: 520 }}>
        <button onClick={() => setOn((v) => !v)} style={{ marginBottom: 10 }}>
          condition is {String(on)} — toggle it
        </button>

        <Conditional on={on} />
        <ConditionalBad on={on} />

        <Suspense fallback={<Box>suspended — waiting for the promise…</Box>}>
          <Message />
        </Suspense>

        {/* A rejection throws DURING render, so it needs an error boundary as
            well as a Suspense boundary. Suspense alone would not catch it. */}
        <Boundary>
          <Suspense fallback={<Box>suspended — waiting for the failing one…</Box>}>
            <Failing />
          </Suspense>
        </Boundary>

        <p style={{ fontSize: 13, color: "#666" }}>
          Toggle the condition with the console open. The first row changes the
          number of hooks it calls and React says nothing. The second does the
          same thing with <code>useContext</code> and React warns that the hook
          order changed — that is the difference the exception is about.
        </p>
      </div>
    </Theme.Provider>
  );
}
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 99 of 119 decoded in the React.js track. One more won't hurt.

Back to track