Module 11 · Custom Hooks
Module 11 · Questions 55–57

Custom Hooks

Short module, but Q57 is a favourite trick question — and being able to write a custom hook on the spot is a strong senior signal.

Q55

What is a Custom Hook?

In one line

A normal function whose name starts with use and which calls other hooks — so you can reuse stateful logic across components.

There's nothing special about it. It's not a React API; it's a pattern.

// before — the same 12 lines in five components
function BrandsPage() {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  useEffect(() => { /* fetch, set, catch, finally */ }, []);
}

// after — write it once
function useFetch(url) {
  const [data, setData]       = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError]     = useState(null);

  useEffect(() => {
    const controller = new AbortController();
    setLoading(true);
    fetch(url, { signal: controller.signal })
      .then(r => { if (!r.ok) throw new Error("HTTP " + r.status); return r.json(); })
      .then(setData)
      .catch(e => { if (e.name !== "AbortError") setError(e.message); })
      .finally(() => setLoading(false));
    return () => controller.abort();
  }, [url]);

  return { data, loading, error };
}

// used anywhere
const { data, loading, error } = useFetch("/api/v1/admin/brands");

The ones worth naming in an interview

HookWhat it wraps
useAuth()Reading the auth context — user, login, logout
useDebounce(value, delay)A timer + cleanup, for search boxes
useLocalStorage(key, initial)State synced to localStorage
useToggle(), useModal()Boolean state + handlers
usePagedList(service)page, pageSize, search, loading, refetch — the whole list-screen pattern

Ten keystrokes, one API call. This is the example to write if they ask you to build a custom hook live — it shows state, effects, cleanup and a real use case in twelve lines.

30-second interview answer

A custom hook is just a function starting with use that calls other hooks, so stateful logic can be shared without duplicating it. It's not a React API, it's a pattern — the replacement for higher-order components and render props. Typical ones I write are useAuth, useDebounce for search inputs, and a usePagedList that holds the page, search and loading state that every list screen repeats. It keeps components focused on rendering.

Q56

Why do custom hooks start with "use"?

In one line

It's the convention that tooling relies on — it tells React's lint rules that the Rules of Hooks apply to this function.

The prefix isn't decoration. eslint-plugin-react-hooks uses it to know that the function may call hooks, so it can check two things:

  • You're not calling hooks conditionally or in a loop inside it.
  • Anything calling it must itself be a component or another hook.
function getUser() {        // no "use" prefix
  const [user] = useState(null);   // a hook in a plain function — the linter can't warn you
}

function useUser() {        // "use" prefix
  const [user] = useState(null);   // checked by the linter
}
30-second interview answer

It's a naming convention that the tooling depends on. React and eslint-plugin-react-hooks use the use prefix to know a function may call hooks, so they can enforce the Rules of Hooks inside it and warn if it's called from a normal function. Without the prefix, violations pass silently and you get confusing runtime bugs.

Q57

Do custom hooks share state automatically?

In one line

No. Each component that calls the hook gets its own separate state. Only the logic is shared, not the data.

function useCounter() { const [n, setN] = useState(0); ... } ComponentA calls useCounter() → its own n (0, 1, 2…) ComponentB calls useCounter() → a DIFFERENT n, completely independent Same recipe, separate kitchens.

Click A five times: B stays at 0. Same hook, separate state.

So how do you actually share the state?

NeedSolution
Share logic onlyCustom hook
Share the same value across componentsContext + a hook that reads it (useAuth)
Share complex app-wide stateRedux / Zustand + a hook wrapper
Share cached server dataReact Query — same key, same cached data
// the standard pattern: hook + context = shared state with a clean API
const AuthContext = createContext(null);
export const useAuth = () => useContext(AuthContext);   // every caller gets the SAME value
30-second interview answer

No — a custom hook shares logic, not state. Every component that calls it gets its own independent state, because the hook runs inside that component. If I want components to share the actual value, the hook has to read from a shared source: a Context provider, a store like Redux or Zustand, or React Query's cache. The usual pattern is a tiny hook that wraps useContext, like useAuth — that gives the clean hook API and genuinely shared state.