目录
Rendered more hooks than during the previous render (React)
渲染了比之前渲染更多的钩子(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.
下面是错误如何发生的示例。
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 钩子只能在顶层调用。
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
。
这意味着我们不允许在循环、条件或嵌套函数中使用钩子。
这是错误如何发生的另一个示例。
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。
将所有钩子移动到组件的顶层
要解决该错误,请将所有挂钩移至组件的顶层,高于任何可能返回值的条件。
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
可能返回值的条件之下。
useState
useEffect
就像
文档中指出的那样:
- 只在顶层调用钩子
- 不要在循环、条件或嵌套函数中调用钩子
- 在任何早期返回之前,始终在 React 函数的顶层使用钩子
- 仅调用来自 React 函数组件或自定义钩子的钩子。
useState
这有助于 React 保留多个和
调用之间的钩子状态useEffect
。
在 React 中渲染的 hooks 比预期错误少
当我们在可能返回值的条件后使用钩子时,会出现错误“渲染的钩子比预期的少。这可能是由意外的提前返回语句引起的”。
要解决该错误,请将所有 React 挂钩移到任何可能返回值的条件之上。
下面是错误如何发生的示例。
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。
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 hooks
我们永远不应该有条件地调用钩子。
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
。
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> ); }
将语句移到if
hook 内部会有所帮助,因为 hook 现在处于顶层并且具有可预测的行为,允许 React 正确地保留useState
和useEffect
调用之间的状态。
就像
文档中指出的那样:
- 只在顶层调用钩子
- 不要在循环、条件或嵌套函数中调用钩子
- 在任何早期返回之前,始终在 React 函数的顶层使用钩子
- 仅从 React 函数组件或自定义钩子调用钩子。
useState
这有助于 React 保留多个和
调用之间的钩子状态useEffect
。
结论
错误“Rendered hooks than expected. This may be caused by an accidental early return statement”意味着我们在组件的第一次渲染上渲染了比重新渲染更多的钩子。
这是由于在使用钩子之前有条件地返回造成的。
要解决该错误,请确保将挂钩移至组件的顶层,并将可能过早返回的所有条件放在底部。
额外资源
您可以通过查看以下教程来了解有关相关主题的更多信息: