Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Uncaught Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement in React Hooks

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>
like image 201
Yangshun Tay Avatar asked Nov 25 '18 22:11

Yangshun Tay


People also ask

How do you fix rendered fewer hooks than expected this may be caused by an accidental early return statement?

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.

How do you solve rendered more hooks than during the previous render?

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.


1 Answers

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 and useEffect 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>
like image 81
Yangshun Tay Avatar answered Sep 23 '22 05:09

Yangshun Tay