In React, from which functions may you call a hook such as useState, and which common call sites are forbidden?
answer
- two legal bodies, nothing else
- the name is the signal
- handlers run after render, not during
- top level of the right function
- conditional arguments are fine
basics
~20 sHooks may be called only from the top level of a React function component body or from another hook (a function whose name starts with "use"). Event handlers, plain helper functions, class components, and nested callbacks are all forbidden.
solid answer
~50 sThere are exactly two legal call sites: the body of a React function component, and the body of a custom hook — a function whose name starts with `use`, which React and the lint plugin both treat as hook-callable. Everything else is out: a click handler, a plain utility function the component calls, a `setTimeout` or `map` callback, the callback you pass to `useEffect`, and any class component method. Within a legal call site the call must also be at the top level: not inside an `if`, a loop, a `&&` or ternary, a `try`/`catch`/`finally` block, or below an early `return`. The reason both halves matter is the same — React identifies hooks by their call order within a rendering component, so a hook must run unconditionally, exactly once per render, from a function React is currently rendering.
go deeper
Name the two legal bodies — a function component and a custom hook — and list the everyday illegal spots: event handlers, plain helpers, and callbacks. That answer alone passes this screener.
Explain why each illegal site fails: no component is rendering at that moment, or the call count would vary. Point at the naming convention as the signal the lint rule keys on.
Show the refactor you would apply in review — read at the top level and close over the value, or promote a helper to a properly named hook — and be able to say when a class component makes a hook migration a rewrite.
Own the convention as an architectural contract: the use prefix is a public promise about a function's call constraints, and codebases that treat it loosely lose static enforcement across every consumer of that function.
## The two legal places **1. The top level of a function component.** A React function component is a function that returns JSX (by convention, capitalized). Hook calls belong directly in its body, before any early return. ```jsx function SearchBox({ query }) { const [text, setText] = useState(query); // legal useEffect(() => { report(text); }, [text]); // legal return <input value={text} onChange={(e) => setText(e.target.value)} />; } ``` **2. The body of another hook.** A function whose name begins with `use` followed by a capital letter is treated as a hook, and hooks may call other hooks. This is what makes custom hooks work at all: the calls inside them simply become part of the calling component's hook list, in the position where the custom hook was called. ```jsx function useOnlineStatus() { const [online, setOnline] = useState(navigator.onLine); // legal: inside a hook return online; } ``` ## The forbidden call sites, and why each fails **Event handlers.** `onClick={() => { const [x] = useState(0); }}` runs long after the render finished. No component is rendering at that moment, so there is no hook list to append to and no cursor to advance — React throws `Invalid hook call.` **The callback passed to useEffect.** Same reason: effect callbacks run after commit, outside render. `useEffect(() => { const value = useContext(Theme); }, [])` is illegal; read the context in the component body and let the effect close over the value. **Plain helper functions.** A function named `formatRow` or `getConfig` is not a component and not a hook, even if the component calls it during render. React cannot tell that its hook calls belong to the caller, and the lint rule flags it. The fix is either to rename it to `useSomething` (making it a real hook, subject to the same rules) or to pass the value in as an argument. **Class components.** Hooks were never wired into the class rendering path; a class has instance fields and lifecycle methods instead. A class cannot call a hook, which is why migrating a class to hooks is a rewrite of the component, not a line-level substitution. **Nested callbacks generally.** `array.map(item => useMemo(...))` is illegal on two counts: it is not a component or hook body, and the number of calls would vary with the array length — the exact instability the ordering scheme forbids. ## Top level, inside the legal site Being in the right function is only half the rule. Inside it, the call must be unconditional: ```jsx function Panel({ user }) { if (!user) return null; const [open, setOpen] = useState(false); // illegal: below an early return } ``` React's documented rules also exclude `try`/`catch`/`finally` blocks — a hook that throws mid-list would leave the list in an inconsistent state. Loops and conditional expressions (`cond && useMemo(...)`) are out for the same call-count reason. What is *not* forbidden is conditional data. `useState(user ? user.name : '')` and `useEffect(fn, [user?.id])` are both fine, because the call itself still runs on every render. ## How this gets enforced The `react-hooks/rules-of-hooks` rule in `eslint-plugin-react-hooks` reports these statically, using the naming convention as its signal: capitalized functions are components, `use`-prefixed functions are hooks, and anything else is neither. That is why the naming convention is load-bearing rather than cosmetic — call it `getWindowSize` instead of `useWindowSize` and the lint rule will report the `useState` inside it as an illegal call. It is configured as an error in the plugin's recommended config, because unlike a style rule, a violation is a real runtime defect. At runtime the enforcement is the dispatcher: React installs the current renderer's hook implementations only for the duration of a component's render, so a hook called from anywhere else finds nothing installed and throws.
- Why does renaming a helper from getWindowSize to useWindowSize change whether hooks are allowed inside it?Because the `use` prefix is the convention React's tooling keys on. The lint rule classifies a `use`-prefixed function as a hook and permits hook calls inside it; anything else is treated as a plain function and its hook calls are reported. Renaming does not change runtime behaviour by itself — but it tells both the linter and human readers that the function must be called from a component or hook, at the top level.
- I need a context value inside a click handler. Where does the useContext call go?In the component body, at the top level, and the handler closes over the result. Handlers run after the render that created them, when no component is rendering, so a hook call there throws. Reading during render and capturing the value in the handler is the standard shape for every hook you need inside an event callback.
saying these in an interview costs you the question
- Says hooks work anywhere as long as the file exports a component
- Thinks a hook is legal inside a useEffect callback
- Believes any helper function called during render may use hooks
- Claims class components can call hooks if they are wrapped
- Treats the use-prefix naming convention as purely cosmetic