React / Editorial solution guide
Diagnose invalid Hook calls and changing Hook counts
Separate call-position defects from duplicate React packages and renderer compatibility before changing dependencies.
Read-only knowledge base · No public comments
Recognize the symptoms
The application reports an invalid Hook call or a message such as “Rendered more hooks than during the previous render.” A component may work in one branch and fail after an early return changes, a feature is toggled, or a local library is linked.
Diagnostic wording varies by version and build. Preserve the complete error and the smallest interaction that changes the path.
Understand the likely causes
- Ordinary Hooks such as useState and useEffect require a consistent top-level call order in components or custom Hooks. Calls in conditions, loops, handlers, or after a changing early return break that contract.
- An invalid-call error can also arise from incompatible React and renderer versions or multiple React module instances. These package causes are distinct from a visible source-order defect and need different evidence.
1. Map the ordinary Hook call sites
Inspect the failing component and every custom Hook it calls. Compare execution paths before and after the triggering interaction, including early returns. Ordinary utility functions are not valid Hook owners merely because a component calls them. Extract a real component or custom Hook where appropriate. Do not invoke a component as App() to bypass JSX and expect its Hook lifecycle to remain correct. React’s special use API has documented conditional-call allowances in supported versions, but those exceptions do not apply to useState or useEffect. Keep the diagnosis specific to the API actually used.
2. Preserve call order across every render
Move ordinary Hooks before conditional returns, put a condition inside an Effect when the synchronization itself is optional, or extract a child component with its own stable lifecycle. The example always calls useState before selecting visible content. A hidden branch does not skip the Hook, so toggling the view does not change the call sequence. This is not a reason to mount expensive unused subscriptions: a conditional child can be the cleaner boundary when a feature should exist only while shown. Preserve state-reset semantics deliberately when making that extraction.
import { useState } from "react";
function Detail({ visible }) { const [count, setCount] = useState(0); if (!visible) return <p>Details hidden</p>; return <button type="button" onClick={() => setCount(c => c + 1)}>Detail count {count}</button>; }
export default function App() { const [visible, setVisible] = useState(false); return <main><h1>Stable Hook order</h1><button type="button" onClick={() => setVisible(x => !x)}>Toggle</button><Detail visible={visible} /></main>; }
Toggle reveals Detail count 0 without a Hook-order error.
The Hook runs in the same position whether Detail returns hidden text or a button. Toggling does not change the Hook count.
3. Inspect installed and resolved module identity
If the source positions are valid, record actual React and renderer versions and inspect the project’s package tree using its existing read-only package-manager diagnostics. A linked library can accidentally bundle its own React instead of sharing the intended peer dependency. The renderer and application must resolve compatible React module identities for the supported setup. Do not delete lockfiles or upgrade every package as a first step; that changes the evidence and may introduce unrelated behavior. Review the bundler’s dependency resolution and library packaging contract, then make a targeted approved dependency correction.
4. Verify source and package repairs separately
Create a tiny isolated component under the same reviewed host and exercise the formerly failing branch. Keep Rules-of-Hooks linting enabled to catch source regressions. Then test the linked-library or workspace integration if package identity was the cause. A passing standalone counter does not prove the original bundler graph is repaired. Check development and production builds according to the project’s support plan, and confirm no warning was merely suppressed. Avoid catching a Hook-order error and continuing normal rendering; repair the violated lifecycle or module boundary.
Verify the fix
The original interaction can toggle all relevant paths without Hook diagnostics, and linting accepts the ordinary Hook call structure. When dependencies were involved, the actual application and linked packages resolve the intended compatible React/renderer setup under its saved lockfile and build configuration.
Keep it from recurring
- Keep Hooks at documented call boundaries, review early returns during refactors, and package reusable libraries with a deliberate React peer-dependency policy. Preserve reproducible lockfiles and targeted module-resolution evidence.