oxc-project/oxc · error
React Hook {hook_name:?} cannot be called inside a callback.
Error message
React Hook {hook_name:?} cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function. What it means
Diagnostic from oxlint's port of react-hooks/rules-of-hooks. This generic variant fires when a React Hook (any `use*` call other than the `use()` builtin) is called inside an anonymous function or arrow used as a function argument (a callback) that itself lives inside a component or custom Hook. Hooks rely on stable call order per render; a callback may run zero, multiple, or out-of-order times, which corrupts React's hook state bookkeeping.
Source
Thrown at crates/oxc_linter/src/rules/react/rules_of_hooks.rs:157
"Hooks in class components should have a containing class span"
);
let mut labels = vec![hook_span.primary_label("Hook is called here")];
if let Some(class_component_span) = class_component_span {
labels.push(class_component_span.label("Class component is defined here."));
}
OxcDiagnostic::warn(format!(
"React Hook {hook_name:?} cannot be called in a class component. React Hooks \
must be called in a React function component or a custom React \
Hook function."
))
.with_labels(labels)
.with_error_code_scope(SCOPE)
}
pub(super) fn generic_error(span: Span, hook_name: &str) -> OxcDiagnostic {
OxcDiagnostic::warn(format!(
"React Hook {hook_name:?} cannot be called inside a callback. React Hooks \
must be called in a React function component or a custom React \
Hook function."
))
.with_label(span.label("This Hook call is inside a nested callback."))
.with_error_code_scope(SCOPE)
}
pub(super) fn use_effect_event_reference(
span: Span,
name: &str,
called: bool,
) -> OxcDiagnostic {
let mut message = format!(
r#"`{name}` is a function created with React Hook "useEffectEvent", and can only be called from Effects and Effect Events in the same component."#
);
if !called {View on GitHub (pinned to e1e7af627c)
Solutions
- Hoist the Hook call to the top level of the component/custom Hook, then close over its result inside the callback.
- For effects that need async/timer logic, keep the hook outside and put only the side effect inside `useEffect(() => {...})`.
- Replace per-item hooks with a child component (`items.map(i => <Row id={i} />)` where Row calls the hook) or a batching hook.
- Only call the same hooks in the same order every render — never behind conditions, loops, or callbacks; if you must gate, gate the callback, not the hook.
Example fix
// before
function List({ ids }) {
return ids.map((id) => {
const data = useQuery(id); // hook inside a callback
return <Row data={data} />;
});
}
// after
function List({ ids }) {
return ids.map((id) => <Row key={id} id={id} />);
}
function Row({ id }) {
const data = useQuery(id); // hook at top level of a component
return <li>{data.name}</li>;
} Defensive patterns
Strategy: validation
Validate before calling
oxlint --react-plugin src/ # rules-of-hooks
# or in CI: npx eslint . --rule '{"react-hooks/rules-of-hooks": "error"}' Prevention
- Only call hooks at the top level of components/custom hooks — never in callbacks, loops, or conditionals.
- Hoist hooks out of .map/timers/handlers; put only side effects inside callbacks.
- Enable react-hooks rules in the editor (fast feedback) and CI (enforcement).
- Note the exception: the `use()` builtin is exempt; classic hooks are not.
When it happens
Trigger: Per the rules_of_hooks run() logic: the Hook's parent function is a `Function { id: None }` or arrow that is a non-React function argument, and `is_somewhere_inside_component_or_hook` is true — e.g. `useEffect(() => { setTimeout(() => { setVisible(true) /* any useX here */ }, 100) })`, `items.map(id => useQuery(id))`, render-prop children ` <Foo>{() => useContext(X)}</Foo>` inside a component, event-handler props like `onClick={() => useToggle()}`. Named helper functions and class components produce sibling diagnostics (function_error / class_component), not this one.
Common situations: Data fetching inside `.map` callbacks (pre-react-query muscle memory); calling setState-ish hooks inside timers/listeners created in useEffect (the setter itself is fine — calling `useState` there is not); conditionally calling hooks behind early-return callbacks; render-props in design-system components.
Related errors
- Empty array binding pattern
- Empty object binding pattern
- ARIA used where native HTML could suffice.
- Using target=`_blank` without rel=`noreferrer` (which implie
- Using target=`_blank` without rel=`noreferrer` or rel=`noope
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/83bf39e20e9f74ac.
Report an issue: GitHub.