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

JavascriptReactjsReact Hooks

Javascript Problem Overview


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>

Javascript Solutions


Solution 1 - Javascript

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>

Solution 2 - Javascript

I have resolved this issue by changing the order of React Hooks.

So I have changed code from

function(){

const [loading ,setLoading] = React.useState(false);

if(loading){
return "loading ....."
}

useEffect(()=>{
  if(condition) {
    doSomething();
  }
}, []);

return <div>component</div>
}

to

function(){

const [loading ,setLoading] = React.useState(false);


useEffect(()=>{
  if(condition) {
    doSomething();
  }
}, []);


if(loading){
return "loading ....."
}
return <div>component</div>
}

So , when loading becomes true then useEffect will not run and that will throw error..but if i declare loading JSX element after useEffect then it will work perfect.

Solution 3 - Javascript

Make sure that you didn't run useEffect conditionally.

For example, if you have some code like the following:

if(condition) {
  useEffect(()=>{
    doSomething();
  }, []);
}

Then change it to

useEffect(()=>{
  if(condition) {
    doSomething();
  }
}, []);

Then the error would not happen.

Solution 4 - Javascript

So I also experienced this error, but it was not related to the hook being wrapped in a condition. Instead it was caused by me having bad key values.

I had a component that moved items from one list to another by dragging and dropping. However, some of those items were locked, so I simply removed the reference to my hook in those elements.

The issue was that I used the index as a key, thus when I dragged a new element on top it got the same key as the locked element, the value of ref was changed and React complained with the above error.

So my answer to those who come across this issue is - check whether you have truly unique keys!

Solution 5 - Javascript

Today i have just met this error, i fixed it by change the position of hook: useEffect() from middle to header of function. And make sure do NOT use hook inside loop.

You can tell React to skip applying an effect if certain values haven’t changed between re-renders. To do so, pass an array as an optional second argument to useEffect:

      useEffect( () => {
        dispatch({type: 'GET_PAGE', slug: Param});
        setPagedata( data );
      });

TO


      useEffect( () => {
        dispatch({type: 'GET_PAGE', slug: Param});
      }, [Param]);

      useEffect( () => {
        setPagedata( data );
      }, [data]);

ANd it works!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionYangshun TayView Question on Stackoverflow
Solution 1 - JavascriptYangshun TayView Answer on Stackoverflow
Solution 2 - JavascriptJayesh LadumorView Answer on Stackoverflow
Solution 3 - JavascriptJeff TianView Answer on Stackoverflow
Solution 4 - JavascriptMagesoeView Answer on Stackoverflow
Solution 5 - JavascriptJamviet.comView Answer on Stackoverflow