注释从这开始~

总览

当咱们有条件地调用一个钩子或在所有钩子运行之前提前返回时,会产生"Rendered more hooks than during the previous render"谬误。为了解决该谬误,将所有的钩子移到函数组件的顶层,以及不要在条件中应用钩子。

这里有个示例用来展现谬误是如何产生的。

// App.jsimport {useEffect, useState} from 'react';export default function App() {  const [counter, setCounter] = useState(0);  // ⛔️ Error: Rendered more hooks than during the previous render.  if (counter > 0) {    // ️ calling React hook conditionally    useEffect(() => {      console.log('hello world');    });  }  return (    <div>      <button onClick={() => setCounter(counter + 1)}>toggle loading</button>      <h1>Hello world</h1>    </div>  );}

代码的问题在于,咱们有条件地调用了useEffect钩子。

顶层调用

为了解决该谬误,咱们必须将条件移到钩子外部。因为React钩子只能在顶层调用。

import {useEffect, useState} from 'react';export default function App() {  const [counter, setCounter] = useState(0);  // ✅ hook is called at top level (not conditionally)  useEffect(() => {    if (counter > 0) {      console.log('hello world');    }  });  return (    <div>      <button onClick={() => setCounter(counter + 1)}>toggle loading</button>      <h1>Hello world</h1>    </div>  );}

咱们将if语句挪动到了useEffect钩子外部。

这就解决了谬误,因为咱们必须确保每次组件渲染时,React钩子都以雷同的程序被调用。

这意味着咱们不容许在循环、条件或嵌套函数中应用钩子。

这里有另外一个示例用来展现谬误是如何产生的。

import {useState} from 'react';export default function App() {  const [counter, setCounter] = useState(0);  // ️ this returns before second hook runs if condition is met  if (counter > 0) {    return <h2>Returning early</h2>;  }  // ⛔️ Error because hook is called conditionally  const [color, setColor] = useState('salmon');  return (    <div>      <button onClick={() => setCounter(counter + 1)}>toggle loading</button>      <h1>Hello world</h1>    </div>  );}

问题在于,第二个useState钩子只有在下面的条件没有满足时才会被调用。

条件之上

为了解决这个谬误,把所有的钩子移到组件的顶层,在任何可能返回值的条件之上。

import {useState} from 'react';export default function App() {  const [counter, setCounter] = useState(0);  const [color, setColor] = useState('salmon');  // ️ condition that may return early must be below all hooks  if (counter > 0) {    return <h2>Returning early</h2>;  }  return (    <div>      <button onClick={() => setCounter(counter + 1)}>toggle loading</button>      <h1>Hello world</h1>    </div>  );}

咱们把第二个useState钩子挪动到有可能返回一个值的if条件下面。

这是很有帮忙的,因为钩子当初在顶层,并且有可预测的行为,容许React在调用useStateuseEffect之间正确地保留状态。

就像文档中所说的那样:

  • 只从React函数组件或自定义钩子中调用Hook
  • 只在最顶层应用 Hook
  • 不要在循环,条件或嵌套函数中调用 Hook
  • 确保总是在你的 React 函数的最顶层以及任何 return 之前应用 Hook

这有助于React在多个useStateuseEffect调用之间保留钩子的状态。