为什么在顶层调用 React Hook?

Rin*_*ala 7 reactjs react-hooks

我正在阅读 React-hooks 概念。我经历了一条规则,上面写着Don't call React hooks inside conditions。他们在这里提供了解释链接

function Form() {
  // 1. Use the name state variable
  const [name, setName] = useState('Mary');

  // 2. Use an effect for persisting the form
  useEffect(function persistForm() {
    localStorage.setItem('formData', name);
  });

  // 3. Use the surname state variable
  const [surname, setSurname] = useState('Poppins');

  // 4. Use an effect for updating the title
  useEffect(function updateTitle() {
    document.title = name + ' ' + surname;
  });

  // ...
}
Run Code Online (Sandbox Code Playgroud)

我明白他们想说什么,但我无法得到确切的原因,比如为什么我不能在 if-else 块中使用 useEffect ?

还有一种说法

So how does React know which state corresponds to which useState call?

useState 每次的调用都是不同的,每次都会返回新的“[state, setState]”,那么知道谁调用了哪个 useState 有什么困难呢?

sub*_*tra 8

这与谁调用了哪个钩子无关useXXXX(即useStateuseEffect等)。它是关于钩子如何在内部实现以及如何与每个组件关联的。React 还有很多其他问题的解决依赖于钩子的调用顺序。

\n

来自文档Hooks FAQ 部分

\n
\n

React 如何将 Hook 调用与组件关联起来?

\n

有一个与每个组件关联的 \xe2\x80\x9c 内存单元\xe2\x80\x9d 的内部列表。它们\xe2\x80\x99只是JavaScript对象,我们可以在其中放置一些数据。当您调用像 useState() 这样的 Hook 时,它会读取当前单元格(或在第一次渲染期间初始化它),然后将指针移动到下一个单元格。这就是多个 useState() 调用每个获得独立本地状态的方式。

\n
\n

内部钩子的实现类似于队列,其中每个钩子代表一个引用下一个钩子的节点。内部结构可能看起来与此类似,

\n
{\n  memoizedState: \'a\',\n  next: {\n    memoizedState: \'b\',\n    next: null\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

以调用 4 次有 4 个状态变量为例useState。对于每个钩子调用,如果该值尚未初始化(即在第一次渲染时),它将初始化从内存单元读取的值,然后在内部移动到下一个钩子。

\n
// 4 independent local state variables with their own "memory cell"\n// nothing is called conditionally so the call order remains the same\n// across renders\nuseState(1)   // 1st call \nuseState(2)   // 2nd call\nuseState(3)   // 3rd call\nuseState(4)   // 4th call\n
Run Code Online (Sandbox Code Playgroud)\n
useState(1)\n\nif (condition) {   // if condition false hook call will be skipped\n  useState(2)   \n}\n\nuseState(3)   \nuseState(4)   \n
Run Code Online (Sandbox Code Playgroud)\n

现在,当您有条件地调用钩子时,如果条件是false钩子调用将被跳过。这意味着每个后续的钩子调用都会在调用顺序中移动 1,从而导致无法读取状态值或替换效果或更多难以检测的错误。

\n

所以一般来说,有条件地调用任何钩子都是一个坏主意。仅在顶层调用钩子(而不是内部条件、嵌套函数或循环),这将有助于 React 为多个钩子调用保留钩子的状态。

\n


Art*_*uel 6

基本上,挂钩依赖于调用索引。React 不知道给定useState()返回了什么,因为它是前一个渲染的状态,但它确实知道useState()该组件的第一次调用返回了 a[1,2]作为它的值,第二次调用返回了false。现在,如果 React 唯一知道的是给定调用索引的给定返回是什么,那么如果我可以编写这样的组件,您认为会发生什么:

const [a, setA] = React.useState([1,2,3]);
let c;
if(a === [3,2,1]){
  c = React.useState('X');
}
const [b, setB] = React.useState(false);
React.useEffect(() => setA([3,2,1]), []);
Run Code Online (Sandbox Code Playgroud)

现在,react 从第一个渲染中知道第一个调用返回 [1,2,3] 而第二个返回 false。然后效果重新渲染组件,现在它不是第一次渲染,因此第一次调用将返回[3,2,1]更新后的状态,第二次调用(第一个c = ...)将返回 false,但随后 React 看到第三次调用,它应该返回什么?

从 React 的角度来看,这是没有意义的,从你的角度来看,这可能会导致大量的错误和问题。

当然,我的基本解释和 React 的解释都不是很多,这就是为什么有来源的原因,Dan Abramov,一位在 React 工作的人,在他的博客上有一篇关于这个的非常长而详细的文章,你可以在这里找到它。他还发布了很多有关 React 在幕后如何工作的其他内容,值得一读。