javascript 未捕获的错误:呈现的钩子比预期的少。这可能是 React Hooks 中意外的提前返回语句导致的

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/53472795/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 10:12:41  来源:igfitidea点击:

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

javascriptreactjsreact-hooks

提问by Yangshun Tay

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:

给定以下组件,当我按下年龄选择器并将值更改为 15,以便呈现没有驾驶执照字段的表单时,我收到错误消息:

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 (react-dom.development.js:18987)
    at batchedUpdates (react-dom.development.js:2210)
    at dispatchEvent (react-dom.development.js:4946)
    at interactiveUpdates (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>

回答by Yangshun Tay

The problem is that in the first render, 3 useStatehooks were invoked - name, ageand licensebut after the age is changed to a value below 16, the useStatefor licenseis no longer invoked, resulting in only the first 2 hooks being invoked. As the React docs state:

的问题是,在第一渲染,3个useState钩被调用- nameagelicense但年龄变更后的值低于16时,useStatelicense不再被调用,导致只有第一2个钩子被调用。正如React 文档所述

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 useStateand useEffectcalls.

不要在循环、条件或嵌套函数中调用 Hook。相反,始终在 React 函数的顶层使用 Hook。通过遵循此规则,您可以确保每次渲染组件时都以相同的顺序调用 Hook。这就是允许 React 在多次调用useStateuseEffect调用之间正确保留 Hook 状态的原因。

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.

调用钩子的顺序很重要,如果我们编写的代码导致钩子不被调用,React 将无法将钩子调用与其值匹配。

The solution is to move the licensehook up to the top of the function so that it gets called regardless whether it is needed or not.

解决方案是将license钩子移动到函数的顶部,以便无论是否需要它都会被调用。

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>

回答by Jeff Tian

Make sure that you didn't run useEffectconditionally.

确保您没有useEffect有条件地运行。

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.

那么错误就不会发生。