Given the following component, when I press down on the age selector and change the value to 15, such that I render a form without the driver license field, I get the error:
Uncaught Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement. at invariant (react-dom.development.js:55) at finishHooks (react-dom.development.js:11581) at updateFunctionComponent (react-dom.development.js:14262) at beginWork (react-dom.development.js:15103) at performUnitOfWork (react-dom.development.js:17817) at workLoop (react-dom.development.js:17857) at HTMLUnknownElement.callCallback (react-dom.development.js:149) at Object.invokeGuardedCallbackDev (react-dom.development.js:199) at invokeGuardedCallback (react-dom.development.js:256) at replayUnitOfWork (react-dom.development.js:17113) at renderRoot (react-dom.development.js:17957) at performWorkOnRoot (react-dom.development.js:18808) at performWork (react-dom.development.js:18716) at flushInteractiveUpdates$1 (react-dom.development.js:18987) at batchedUpdates (react-dom.development.js:2210) at dispatchEvent (react-dom.development.js:4946) at interactiveUpdates$1 (react-dom.development.js:18974) at interactiveUpdates (react-dom.development.js:2217) at dispatchInteractiveEvent (react-dom.development.js:4923)
Example code below:
const {useState} = React; function App() { const [name, setName] = useState('Mary'); const [age, setAge] = useState(16); if (age < 16) { return ( <div> Name:{' '} <input value={name} onChange={e => { setName(e.target.value); }} /> <br /> Age:{' '} <input value={age} type="number" onChange={e => { setAge(+e.target.value); }} /> </div> ); } const [license, setLicense] = useState('A123456'); return ( <div> Name:{' '} <input value={name} onChange={e => { setName(e.target.value); }} /> <br /> Age:{' '} <input value={age} type="number" onChange={e => { setAge(+e.target.value); }} /> <br /> Driver License:{' '} <input value={license} onChange={e => { setLicense(e.target.value); }} /> </div> ); } ReactDOM.render(<App />, document.querySelector('#app'));
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script> <script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script> <div id="app"></div>
The error "Rendered fewer hooks than expected. This may be caused by an accidental early return statement" occurs when we use a hook after a conditional that may return a value. To solve the error, move all React hooks above any conditionals that may return a value.
The error "Rendered more hooks than during the previous render" 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.
The problem is that in the first render, 3 useState
hooks were invoked - name
, age
and license
but after the age is changed to a value below 16, the useState
for license
is no longer invoked, resulting in only the first 2 hooks being invoked. As the React docs state:
Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple
useState
anduseEffect
calls.
The order of the hooks being called is important, and if we write code that causes hooks to not be called, React will not be able to match the hook call with its values.
The solution is to move the license
hook up to the top of the function so that it gets called regardless whether it is needed or not.
const {useState} = React; function App() { const [name, setName] = useState('Mary'); const [age, setAge] = useState(16); const [license, setLicense] = useState('A123456'); return ( <div> Name:{' '} <input value={name} onChange={e => { setName(e.target.value); }} /> <br /> Age:{' '} <input value={age} type="number" onChange={e => { setAge(+e.target.value); }} /> {age >= 16 && <span> <br /> Driver License:{' '} <input value={license} onChange={e => { setLicense(e.target.value); }} /></span> } </div> ); } ReactDOM.render(<App />, document.querySelector('#app'));
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script> <script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script> <div id="app"></div>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With