Skip to solution
mediumFrontend

What are React custom hooks and when should you use them?

820 views
01

Understand the problem

Question presented to candidate: "What makes something a custom hook, and how do you decide when to extract one?"

What a strong answer should cover:

  • A custom hook is just a function whose name starts with use and which calls other hooks. There is no registration and no special API.
  • The naming convention is load-bearing: it is how the ESLint plugin knows to enforce the Rules of Hooks inside it.
  • It shares logic, never state. Every component calling the hook gets its own completely independent state — the single most important thing to say.
  • It obeys the Rules of Hooks: call it unconditionally, at the top level, from a component or another hook.
  • Why extract one: reusing stateful logic, making a component readable, or making the logic testable on its own.
  • What it replaced: HOCs and render props, without the wrapper component, prop collisions, or nesting.
  • Composition: hooks call other hooks, so behaviour composes flatly rather than by nesting.
  • Return shape: an array when order matters and the caller renames (like useState), an object when there are several named values.
  • When not to: a one-off used in a single place, or a wrapper that just renames a built-in hook.

Clarifying questions expected:

  • "Is this logic actually reused, or is extraction just for readability?" — both are valid, but they are different arguments.

Code / implementation expected: Yes — a custom hook with state and an effect, used by two components to show state independence.

hooksreusability
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 and useEffect. Difficulty: Medium

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. The state-independence result in section 3 was p

Solution ready — 2 min read

Classified // press E to declassify

04

Explore the playground snippets

One hook, two components, two entirely separate states
Run Playground
import { useState, useEffect, useCallback } from "react";

// A custom hook: a use-prefixed function calling other hooks. Nothing more.
function useCounter(start = 0, label = "") {
  const [count, setCount] = useState(start);

  // Stable identities so consumers can safely memoise.
  const inc = useCallback(() => setCount((c) => c + 1), []);
  const reset = useCallback(() => setCount(start), [start]);

  useEffect(() => {
    if (count > 0) console.log(label + " is now " + count);
  }, [count, label]);

  return { count, inc, reset };
}

// Hooks COMPOSE: a custom hook may call other custom hooks, and it stays flat.
function useDoubledCounter(start, label) {
  const counter = useCounter(start, label);
  return { ...counter, doubled: counter.count * 2 };
}

function Panel({ title, hook }) {
  const { count, doubled, inc, reset } = hook;
  return (
    <div style={{ border: "1px solid #ddd", borderRadius: 8, padding: 12, flex: 1 }}>
      <h4 style={{ margin: "0 0 8px" }}>{title}</h4>
      <p style={{ margin: "0 0 8px" }}>
        count: <strong>{count}</strong>
        {doubled !== undefined && <> · doubled: <strong>{doubled}</strong></>}
      </p>
      <button onClick={inc}>+1</button> <button onClick={reset}>reset</button>
    </div>
  );
}

function CounterA() {
  // Each call to the hook creates its OWN state. A and B never interact.
  return <Panel title="Component A" hook={useCounter(0, "A")} />;
}

function CounterB() {
  return <Panel title="Component B" hook={useCounter(0, "B")} />;
}

function CounterC() {
  return <Panel title="Component C (composed hook)" hook={useDoubledCounter(10, "C")} />;
}

export default function App() {
  return (
    <div style={{ padding: 24, fontFamily: "system-ui", lineHeight: 1.6 }}>
      <div style={{ display: "flex", gap: 12, marginBottom: 12 }}>
        <CounterA />
        <CounterB />
      </div>
      <CounterC />
      <p style={{ color: "#666", fontSize: 13 }}>
        Click +1 on A repeatedly. B does not move, and neither does C. The hook
        shares the logic; each component gets its own independent state. If you
        wanted them to share a value, that would be lifted state or Context.
      </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 46 of 119 decoded in the React.js track. One more won't hurt.

Back to track