Blog

Blog · Error Fixes

How to Fix React Hook Errors (Invalid Hook Call & More)

Invalid hook call, missing dependency, "rendered more hooks than…", too many re-renders — the four React Hook errors developers hit most, with the real cause and fix for each.

XCODX Team · 4 min read

React tracks hook state by call order, and only while a function component is rendering. Break either rule — or wire React up wrong — and you get one of these four errors. Here is what actually triggers each and how to fix it.

Invalid hook call

Invalid hook call. Hooks can only be called inside of the body of a function component. React lists three documented causes: mismatching React / React-DOM versions, breaking the Rules of Hooks, or more than one copy of React in the app.

// most common: two copies of React (diagnose with `npm ls react`)
// A library that bundles its own React breaks the host app's hooks.

// ❌ library package.json
"dependencies": { "react": "^18.2.0" }

// ✅ declare React as a peer (+ dev) dependency so the host copy is shared
"peerDependencies": { "react": ">=16.8.0" },
"devDependencies":  { "react": "^18.2.0" }
// also: never call a component as a function — that runs its hooks
// outside React's render.
// ❌
return <div>{Child()}</div>;
// ✅
return <div><Child /></div>;

React Hook useEffect has a missing dependency

An ESLint warning (react-hooks/exhaustive-deps), not a runtime error: the effect reads a reactive value that is not in its dependency array, so it will run with a stale value and not re-run when that value changes.

// ❌ reads userId but does not list it
useEffect(() => { fetchUser(userId); }, []);
// ✅
useEffect(() => { fetchUser(userId); }, [userId]);

// a function you define in the component: memoize it
const load = useCallback(() => setData(query(id)), [id]);
useEffect(() => { load(); }, [load]);

// or drop the dependency legitimately with a functional update
useEffect(() => { setCount(c => c + 1); }, []); // no `count` needed

Rendered more (or fewer) hooks than the previous render

Rendered more hooks than during the previous render (or "fewer… caused by an accidental early return"). Because hooks are identified by call order, every render must call the same hooks in the same sequence — a conditional or early return that skips a hook breaks this.

// ❌ early return before a hook
function Profile({ user }) {
  if (!user) return null;
  const [tab, setTab] = useState("info");
}
// ✅ all hooks first, guards after
function Profile({ user }) {
  const [tab, setTab] = useState("info");
  if (!user) return null;
}

The fix is always structural: put every hook unconditionally at the top of the component, and place conditionals or early returns strictly below them. To conditionalize behavior, move the condition *inside* the hook (e.g. inside the useEffect body).

Too many re-renders

Too many re-renders. React limits the number of renders to prevent an infinite loop. A state update is being triggered during render itself — most often by *calling* a setter instead of passing a function.

// ❌ setCount is CALLED during render
<button onClick={setCount(count + 1)}>+</button>
// ✅ pass a function
<button onClick={() => setCount(count + 1)}>+</button>

// ❌ setState in the component body -> loop
setFull(`${first} ${last}`);
// ✅ derive it during render, no state needed
const full = `${first} ${last}`;

Quick diagnostic checklist

  1. Invalid hook call: npm ls react / npm ls react-dom — exactly one each, versions aligned; hooks only at the top level of a component or a use* hook.
  2. Missing dependency: list every reactive value the effect reads and add each to the array; memoize functions, depend on primitive fields, or use a functional updater.
  3. More/fewer hooks: scan for any useX inside if, for, ? :, && or a return above a hook — move all hooks above the first conditional return.
  4. Too many re-renders: check every onClick/onChange — is the setter passed or called? Move setState out of the render body.

Frequently asked questions

What is the most common cause of "Invalid hook call"?
Having more than one copy of React in the project — the app’s useState and react-dom’s internal dispatcher then come from different module instances. Run npm ls react; if you see two, dedupe, and make libraries declare React as a peer dependency.
Should I ignore the "missing dependency" warning?
No. It flags an effect that will run with a stale value. Add the dependency, memoize functions with useCallback, depend on primitive fields, or use a functional updater setX(prev => …). Disabling the rule hides a real bug.
Why do I get "Too many re-renders"?
A setter is being called during render — commonly onClick={setX(...)} instead of onClick={() => setX(...)}, or setState in the component body. Pass a function to the handler, or derive the value during render instead of storing it.

Spin up a React sandbox in XCODX Studio to reproduce any of these — it compiles JSX in the browser with a live preview, so you can watch the fix take effect without a local toolchain.