React Error #310: Rendered more hooks than during the previous render
Rendered more hooks than during the previous render
Verified against React 19 source (ReactFiberHooks.js), React docs: rules-of-hooks, eslint-plugin-react-hooks docs · Updated April 2026
> quick_fix
A component called more (or fewer) hooks than on its previous render. The most common cause is an early return or a conditional placed above a hook call. Move every hook to the top of the component, before any if-return, loop, or try-catch.
// Causes #310 - hook below early return
function Bad({ user }) {
if (!user) return null
const [n, setN] = useState(0) // skipped on first render
return <p>{n}</p>
}
// Fix - hook above any return
function Good({ user }) {
const [n, setN] = useState(0)
if (!user) return null
return <p>{n}</p>
}What causes this error
React tracks hooks by call order, not by name. Each render must call exactly the same hooks in exactly the same order, so React can match the new render's hook list to the previous render's hook state. When a conditional skips a hook on one render but not the next, the order changes and React throws invariant #310.
How to fix it
- 01
step 1
Find the component named in the trace
React's stack trace points at the component that rendered a different number of hooks. Open it and look for any hook below a conditional return.
- 02
step 2
Move every useState, useEffect, useMemo, useCallback, useRef, useReducer, useContext, and custom hook to the very top
Before any if, return, ?:, &&, ||, try, catch, for, or while. The Rules of Hooks is strict about this and #310 is the strict enforcement.
- 03
step 3
Move conditional logic into the hook, not around it
Instead of conditionally calling useEffect, put the condition inside the effect. Same for useMemo and useCallback.
// Bad if (enabled) useEffect(() => { ... }, []) // Good useEffect(() => { if (!enabled) return // ... }, [enabled]) - 04
step 4
If the offending call is in a custom hook, follow the same rule recursively
Custom hooks must also call their inner hooks in the same order every render. Lint with eslint-plugin-react-hooks to catch these statically.
Why #310 happens at the runtime level
React stores hook state in a singly-linked list attached to each component's Fiber. On every render, the dispatcher walks this list in call order, returning the same memoryCell for each useState, useEffect, etc. The list is keyed by position, not by hook name. When a conditional renders skip a hook on one render but include it on the next, the dispatcher reads the wrong slot and would corrupt unrelated state. ReactFiberHooks.js detects the length mismatch via the hook counter and throws invariant #310 before any state can be misassigned.
Common debug mistakes for #310
- Adding a useMemo inside an if block to 'optimise' performance, then wondering why production crashes - the hook is called on truthy renders only.
- Wrapping a hook in try-catch hoping to handle a thrown error, not realising the catch path skips the hook on the next render.
- Conditionally returning JSX above a custom hook call - the custom hook contains hooks of its own and the whole chain gets skipped.
- Using && short-circuit before a hook (enabled && useEffect(...)) - same skip-pattern as if-return, just less obvious in code review.
- Disabling the eslint-plugin-react-hooks rule with eslint-disable to ship faster - the runtime error is harder to debug than the lint warning.
When #310 signals a deeper problem
Persistent #310 usually means components are doing too much - mixing data fetching, state management, and rendering inside one function so that early-return shortcuts feel necessary. The architectural fix is to split the component into a guard component (handles the conditional return) and a content component (always renders, hooks always run). This pattern shows up in route components, auth-gated pages, and feature-flag wrappers where the cleanest version is two components rather than one with conditionals around hooks.
Frequently asked questions
Why does React enforce the Rules of Hooks so strictly?
Hooks store their state in a linked list keyed by call order. If the order changes between renders, React would silently mismatch state to hooks, leading to corruption that's impossible to debug. Invariant #310 catches it at the source.
Can I use early-return components at all?
Yes - just call all your hooks first, then return. Hooks above the return, JSX or null below.
Does this rule apply to render props or HOCs?
Inside a component or custom hook, yes. The wrapping HOC is a separate component with its own hook order.