React Hooks的提前归还问题

时间:2019-11-24 07:06:41

标签: reactjs react-hooks

我已经使用React钩子遇到了Invariant violation: rendered fewer hooks than expected问题。从其他答案中可以明显看出,不应有条件地调用钩子(Uncaught Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement in React Hooks)。

导致应用崩溃的情况:

const MyComponent = (item) => {
    const [itemState, setState] = useState(true);

    if (!item) {
        return null;
    }

    const [anotherState, setAnotherState] = useState(true);

    return (<div>{item.name}</div>)
}

作为尝试,我试图通过在调用钩子之前移动支票来修复它,例如:

const MyComponent = (item) => {

    if (!item) {
        return null;
    }

    const [itemState, setState] = useState(true);
    const [anotherState, setAnotherState] = useState(true);

    return (<div>{item.name}</div>)
}

这似乎有效,并且从未崩溃。 我决定安装eslint-plugin-react-hooks以防止将来出现类似情况。现在它以React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?

警告

所以我的问题是:我应该始终执行返回after的所有钩子吗?例如:

const MyComponent = (item) => {

    const [itemState, setState] = useState(true);
    const [anotherState, setAnotherState] = useState(true);

    if (!item) {
        return null;
    }

    return (<div>{item.name}</div>)
}

如果是,为什么然后如果我先返回between和第二个钩子就会崩溃,而如果我返回before所有的钩子却不会崩溃呢?

2 个答案:

答案 0 :(得分:0)

声明您在顶部无条件地使用useState-

 const MyComponent = ({item}) => {
  const [itemState, setState] = useState(true);
  const [anotherState, setAnotherState] = useState(true);
  const [localItem, setLocalItem] = useState(item);
  return localItem == null ? <></> : <div>{item.name}</div>;
};

在此处查找规则-https://reactjs.org/docs/hooks-overview.html#rules-of-hooks

答案 1 :(得分:0)

如果在提前返回之前或之后移动所有钩子,则每个渲染上始终具有相同数量的钩子(无或2个)。如果您先放一个钩子,然后再放一个钩子,那么您就不放了,当它返回得早时,您将有一个钩子,而当您不放钩子时,将有2个钩子。

提早返回后放置useState会使linter感到困惑,但这也会破坏您的状态。当您提早返回时,此状态将重置,而在下次渲染时则不重置。

在以下示例中,将一个点添加到“ Hello World”中,当您关闭然后再打开时,所有点都消失了。在之前之前定义setState可以让您保持满意。

const { useState } = React;
function App({ wut }) {
  const [show, setShow] = useState(true);
  return (
    <div>
      <button onClick={() => setShow(s => !s)}>
        toggle
      </button>
      <MyComponent item={show} />
    </div>
  );
}
const MyComponent = ({ item }) => {
  if (!item) {
    return <div>no item</div>;
  }
  const [itemState, setItemState] = useState('Hello World');
  return (
    <div>
      {itemState}
      <button onClick={() => setItemState(s => s + '.')}>
        Add .
      </button>
    </div>
  );
};
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>