Yangshun Tay
Yangshun Tay

Reputation: 53169

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>

Upvotes: 140

Views: 189853

Answers (12)

Sid
Sid

Reputation: 19

This error occurs because you shouldn't use hooks like state inside loops. Instead try to go for local variables. Also if you are handling promises or getting async data which cannot be handled through variables, then create a separate component for that, and then you can successfully use your hooks inside map function or loops without getting any error.

Upvotes: 1

samloba
samloba

Reputation: 33

In my case I was using useWindowDimensions hook directly in the return part of functional component with condition. Solve it by declaring a variable outside the return part and using the variable in the conditional rendering. Hope this can help!

Upvotes: 1

huseyinsade
huseyinsade

Reputation: 41

We get this error when we call useEffect or useState in the loop. To prevent this, we must convert the method containing that loop into a component and call it in the main component.

Upvotes: 1

Abdul Rehman Kaim Khani
Abdul Rehman Kaim Khani

Reputation: 1170

const useStyle = makeStyles((theme) => ({
  subHeading: {
    lineHeight: 1.2
  },
  listItemTextStyle: {
    textTransform: "none"
  }
}));

export const getOmniDetails = (data) => {
  const classes = useStyle(); 

  return (
    <Typography className={classes.subHeading} component="p">
      User Name: {data.userName}
    </Typography>
  );
};

export default function OmniSearchDropdown(props) {
  const { users = [], openProfile } = props;
  const classes = useStyle();
  return (
    <ListItemText
      primary={
        <Typography variant="h6" className={classes.listItemTextStyle}>
          {r.displayName}
        </Typography>
      }
      secondary={getOmniDetails(r)} // wrong
    />
  );
}

I faced the same issue while I was using the single theme (useStyle) in two different components and I was calling getOmniDetails() as a function which is wrong.

I solved this issue by just converting the function into component <GetOmniDetails data={data}/>.

Upvotes: 1

Jayesh Ladumor
Jayesh Ladumor

Reputation: 601

I have resolved this issue by changing the order of React Hooks. The useEffect came after an early return.

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.

Upvotes: 60

Maxim Stykow
Maxim Stykow

Reputation: 101

This can also happen if you have hot reload (HMR) turned on and you're adding or deleting a useEffect. Just reload the page and the error will not be reproduced.

Upvotes: 0

Ahmet Firat Keler
Ahmet Firat Keler

Reputation: 4040

If you conditionally render, do not put component property data in between!

if (splashScreen)
  return <Spinner>Loading</Spinner>

//DO NOT PUT THEM HERE

return (
  ...
)

Upvotes: 1

KARTHIKEYAN.A
KARTHIKEYAN.A

Reputation: 20118

I have faced the above error in my own react application, when I used the useCallback hook inside the render component (and crucially, within an if condition). Moving the hook outside of functional component resolved the error. The lesson: don't use hooks inside conditional statements.

Upvotes: 1

Jamviet.com
Jamviet.com

Reputation: 306

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!

Upvotes: 4

Magesoe
Magesoe

Reputation: 145

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!

Upvotes: 9

Jeff Tian
Jeff Tian

Reputation: 5903

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.

Upvotes: 17

Yangshun Tay
Yangshun Tay

Reputation: 53169

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>

Upvotes: 150

Related Questions