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

前端 未结 4 1535
暖寄归人
暖寄归人 2021-01-07 18:06

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:

相关标签:
4条回答
  • 2021-01-07 18:31

    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.

    0 讨论(0)
  • 2021-01-07 18:33

    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!

    0 讨论(0)
  • 2021-01-07 18:35

    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/react@16.7.0-alpha.0/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>
    <div id="app"></div>

    0 讨论(0)
  • 2021-01-07 18:53

    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.

    0 讨论(0)
提交回复
热议问题