React hooks的规则

hooks的声明要在顶部

不要再循环语句、条件语句或者嵌套函数中声明hooks。相反,在任何返回之前,总是在React函数的顶层声明hook。通过遵循此规则,您可以确保在每次呈现组件时以相同的顺序调用hook。这使得React能够在多个useState和useEffect调用之间正确保存hook的状态。(如果你感到好奇,我们将在下面深入解释。)

原因

如之前所述,我们可以在单一组件中声明多个state或者Effect

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;
  });

  // ...
}

那么React如何知道哪个状态对应于哪个useState调用呢?答案是React依赖于hook的调用顺序。我们的例子之所以有效,是因为Hook调用的顺序在每次渲染中都是一样的:

// ------------
// First render
// ------------
useState('Mary')           // 1. Initialize the name state variable with 'Mary'
useEffect(persistForm)     // 2. Add an effect for persisting the form
useState('Poppins')        // 3. Initialize the surname state variable with 'Poppins'
useEffect(updateTitle)     // 4. Add an effect for updating the title

// -------------
// Second render
// -------------
useState('Mary')           // 1. Read the name state variable (argument is ignored)
useEffect(persistForm)     // 2. Replace the effect for persisting the form
useState('Poppins')        // 3. Read the surname state variable (argument is ignored)
useEffect(updateTitle)     // 4. Replace the effect for updating the title

// ...

只要Hook调用在渲染之间的顺序相同,React就可以将一些局部状态与它们关联起来。但是,如果我们在条件中放入一个Hook调用(例如,persistForm Effect),会发生什么呢?

  //  We're breaking the first rule by using a Hook in a condition
  if (name !== '') {
    useEffect(function persistForm() {
      localStorage.setItem('formData', name);
    });
  }

在第一次渲染时,name !== ’ ’ 条件为真,所以我们运行这个Hook。但是,在下一次呈现时,用户可能会clear the form,使条件为假。现在我们在渲染过程中跳过了这个Hook, Hook调用的顺序就变了:

useState('Mary')           // 1. Read the name state variable (argument is ignored)
// useEffect(persistForm)  //  This Hook was skipped!
useState('Poppins')        //  2 (but was 3). Fail to read the surname state variable
useEffect(updateTitle)     //  3 (but was 4). Fail to replace the effect

React不知道第二次调用useState Hook要返回什么。React认为该组件中的第二个Hook调用对应于persistForm Effect,就像之前的渲染一样,但现在不是这样了。从那时起,在我们跳过的那个Hook调用之后的每一个Hook调用也会转移一个,从而导致bugs。

这就是为什么必须在组件的顶层调用hook的原因。如果我们想要有条件地运行一个Effect,我们可以把这个条件放到hook中:

  useEffect(function persistForm() {
    //  We're not breaking the first rule anymore
    if (name !== '') {
      localStorage.setItem('formData', name);
    }
  });

注意,如果使用提供的lint规则,则不需要担心这个问题。但是现在您也知道了hook为什么会以这种方式工作,以及规则阻止了哪些问题。

原文章

你可能感兴趣的:(React,react.js,javascript,前端)