渲染了比之前渲染更多的钩子(React)

目录

Rendered more hooks than during the previous render (React)

  1. 渲染了比上一次渲染更多的钩子
  2. 渲染的钩子比预期的少

渲染了比之前渲染更多的钩子(React)

当我们有条件地调用一个钩子或在所有钩子运行之前提前返回时,会发生错误“渲染的钩子比上一次渲染更多”。

要解决该错误,请将所有挂钩移到功能组件的顶层,不要在条件内部使用挂钩。

渲染了比之前渲染更多的钩子

Uncaught Error: Rendered more hooks than during the previous render. React has detected a change in the order of Hooks called by App. This will lead to bugs and errors if not fixed.

下面是错误如何发生的示例。

应用程序.js
import {useEffect, useState} from 'react'; export default function App() { const [counter, setCounter] = useState(0); // ⛔️ Error: Rendered more hooks than during the previous render. // React has detected a change in the order of Hooks // called by App. This will lead to bugs and errors if not fixed. 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 检测到 X 调用的 Hooks 的顺序发生了变化。如果不修复,这将导致错误和错误”

错误的解决方法是一样的。

反应已检测到挂钩顺序的变化

将条件移动到钩子内部

为了解决错误,我们必须将条件移动到钩子内部,因为
React 钩子只能在顶层调用

应用程序.js
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 hooks。

这意味着我们不允许在循环、条件或嵌套函数中使用钩子。

这是错误如何发生的另一个示例。

应用程序.js
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钩子只有在上面的条件不满足时才会被调用。

我还写了一篇详细的指南来解决错误
React Hook ‘useState’ is called conditionally error

将所有钩子移动到组件的顶层

要解决该错误,请将所有挂钩移至组件的顶层,高于任何可能返回值的条件。

应用程序.js
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 函数的顶层使用钩子
  • 仅调用来自 React 函数组件或自定义钩子的钩子。

useState这有助于 React 保留多个和
调用
之间的钩子状态
useEffect

在 React 中渲染的 hooks 比预期错误少

当我们在可能返回值的条件后使用钩子时,会出现错误“渲染的钩子比预期的少。这可能是由意外的提前返回语句引起的”。

要解决该错误,请将所有 React 挂钩移到任何可能返回值的条件之上。

呈现的钩子比预期的少

下面是错误如何发生的示例。

应用程序.js
import {useEffect, useState} from 'react'; export default function App() { const [counter, setCounter] = useState(0); // 👇️ may return a value before hook below runs if (counter > 0) { return <h1>Hello world</h1>; } // ⛔️ Rendered fewer hooks than expected. // This may be caused by an accidental early return statement const [message, setMessage] = useState(''); return ( <div> <button onClick={() => setCounter(counter + 1)}>Increment count</button> </div> ); }

代码示例中的问题是我们useState在可能返回值的条件之后使用了第二个挂钩。

只在顶层调用 React hooks

要解决这个错误,我们必须只
在顶层调用 React hooks

应用程序.js
import {useEffect, useState} from 'react'; export default function App() { const [counter, setCounter] = useState(0); // 👇️ move hooks above condition that might return const [message, setMessage] = useState(''); // 👇️ any conditions that might return must be below all hooks if (counter > 0) { return <h1>Hello world</h1>; } return ( <div> <button onClick={() => setCounter(counter + 1)}>Increment count</button> </div> ); }

我们将第二个useState钩子移到可能返回值的条件之上。

这解决了错误,因为我们必须确保每次组件渲染时都以相同的顺序调用 React hooks。

这意味着我们不允许在循环、条件或嵌套函数中使用钩子。

我们必须确保组件中的所有 React 钩子在每次渲染时都以相同的顺序被调用。

不要有条件地调用 React hooks

我们永远不应该有条件地调用钩子。

应用程序.js
import {useEffect, useState} from 'react'; export default function App() { const [counter, setCounter] = useState(0); if (counter > 0) { // ⛔️ Error useEffect(() => { console.log('hello world'); }, []); } return ( <div> <button onClick={() => setCounter(counter + 1)}>Increment count</button> </div> ); }

代码示例导致错误,因为useEffect挂钩是有条件地调用的。

为了解决这个问题,我们可以将if语句移到钩子内部useEffect

应用程序.js
import {useEffect, useState} from 'react'; export default function App() { const [counter, setCounter] = useState(0); useEffect(() => { if (counter > 0) { console.log('hello world'); } }, [counter]); return ( <div> <button onClick={() => setCounter(counter + 1)}>Increment count</button> </div> ); }

将语句移到ifhook 内部会有所帮助,因为 hook 现在处于顶层并且具有可预测的行为,允许 React 正确地保留useStateuseEffect调用之间的状态。

就像
文档中指出的那样:

  • 只在顶层调用钩子
  • 不要在循环、条件或嵌套函数中调用钩子
  • 在任何早期返回之前,始终在 React 函数的顶层使用钩子
  • 仅从 React 函数组件或自定义钩子调用钩子。

useState这有助于 React 保留多个和
调用
之间的钩子状态
useEffect

结论

错误“Rendered hooks than expected. This may be caused by an accidental early return statement”意味着我们在组件的第一次渲染上渲染了比重新渲染更多的钩子。

这是由于在使用钩子之前有条件地返回造成的。

要解决该错误,请确保将挂钩移至组件的顶层,并将可能过早返回的所有条件放在底部。

额外资源

您可以通过查看以下教程来了解有关相关主题的更多信息: