Borislav Hadzhiev
Tue Apr 05 2022·3 min read
Photo by Jakob Owens
The error "React has detected a change in the order of Hooks called" occurs when we use hooks in conditionals or conditionally return before a hook is called. To solve the error, move all React hooks above any conditionals that may return a value.
Here is an example of how the error occurs.
import {useEffect, useState} from 'react'; export default function App() { const [counter, setCounter] = useState(0); // ⛔️ React has detected a change in the order of Hooks // called by App. This will lead to bugs and errors if not fixed. if (counter > 0) { // 👇️ calling hook conditionally useEffect(() => { console.log('hello world'); }, [counter]); } return ( <div> <button onClick={() => setCounter(counter + 1)}>Increment count</button> </div> ); }
The issue in the code snippet above is that we are conditionally calling the
useEffect
hook.
To solve the error, we have to move the condition inside of the hook because React hooks can only be called at the top level.
import {useEffect, useState} from 'react'; export default function App() { const [counter, setCounter] = useState(0); // ✅ hook is called at top level (not conditionally) useEffect(() => { if (counter > 0) { console.log('hello world'); } }, [counter]); return ( <div> <button onClick={() => setCounter(counter + 1)}>Increment count</button> </div> ); }
We moved the if
statement inside of the useEffect
hook.
This solves the error because we have to ensure that React hooks are called in the same order each time a component renders.
This means that we aren't allowed to use hooks inside loops, conditions or nested functions.
Here is another example of how the error occurs.
import {useState} from 'react'; export default function App() { const [counter, setCounter] = useState(0); // ⛔️ React has detected a change in the order of Hooks // called by App. This will lead to bugs and errors if not fixed. if (counter > 0) { return <h1>Counter is greater than 0</h1>; } // 👇️ this hook only runs if counter is not greater than 0 const [message, setMessage] = useState('hello'); return ( <div> <button onClick={() => setCounter(counter + 1)}>Increment count</button> </div> ); }
The issue in the code snippet is that the second useState
hook is only invoked
if the condition above it isn't met.
To solve the error, we have to move all hooks at the top level of the component, above any conditions that might return a value.
import {useState} from 'react'; export default function App() { const [counter, setCounter] = useState(0); const [message, setMessage] = useState('hello'); // 👇️ condition that may return must be below all hooks if (counter > 0) { return <h1>Counter is greater than 0</h1>; } return ( <div> <button onClick={() => setCounter(counter + 1)}>Increment count</button> </div> ); }
We moved the second useState
hook below the if
condition that could
potentially return a value.
This helps because the hook is now at the top level and has predictable behavior
that allows React to correctly preserve the state between useState
and
useEffect
calls.
Like the documentation states:
This helps React preserve the state of hooks between multiple useState
and
useEffect
calls.
The error "React has detected a change in the order of Hooks called by" occurs when we conditionally call a hook or return early before all hooks have ran. To solve the error, move all hooks at the top level of the function component and don't use hooks inside conditions.