我有一个看起来像这样的组件(非常简化的版本):
const component = (props: PropTypes) => {
const [allResultsVisible, setAllResultsVisible] = useState(false);
const renderResults = () => {
return (
<section>
<p onClick={ setAllResultsVisible(!allResultsVisible) }>
More results v
</p>
{
allResultsVisible &&
<section className="entity-block--hidden-results">
...
</section>
}
</section>
);
};
return <div>{ renderResults() }</div>;
}
当我加载使用此组件的页面时,出现此错误:Uncaught Invariant Violation: Rendered more hooks than during the previous render.
我试图找到此错误的解释,但我的搜索没有返回任何结果。
当我稍微修改组件时:
const component = (props: PropTypes) => {
const [allResultsVisible, setAllResultsVisible] = useState(false);
const handleToggle = () => {
setAllResultsVisible(!allResultsVisible);
}
const renderResults = () => {
return (
<section>
<p onClick={ handleToggle }>
More results v
</p>
{
allResultsVisible &&
<section className="entity-block--hidden-results">
...
</section>
}
</section>
);
};
return <div>{ renderResults() }</div>;
}
我不再收到该错误。是因为我在renderResults
返回的 jsx 中包含setState
函数吗?最好能解释为什么修复程序有效。
我遇到了同样的问题。我所做的是这样的:
const Table = (listings) => {
const {isLoading} = useSelector(state => state.tableReducer);
if(isLoading){
return <h1>Loading...</h1>
}
useEffect(() => {
console.log("Run something")
}, [])
return (<table>{listings}</table>)
}
我认为发生的事情是,在第一次渲染时,组件提前返回并且 useEffect 没有运行。当 isLoading 状态更改时,useEffect 运行并且我得到了错误 - 钩子渲染的次数比之前的渲染次数更多。
一个简单的更改修复了它:
const Table = (listings) => {
const {isLoading} = useSelector(state => state.tableReducer);
useEffect(() => {
console.log("Run something")
}, [])
if(isLoading){
return <h1>Loading...</h1>
}
return (<table>{listings}</table>)
}
该修复程序之所以有效,是因为第一个代码示例(出错的代码示例)调用onClick
内部的函数,而第二个(工作示例)将函数传递给onClick
。区别在于那些非常重要的括号,在 JavaScript 中表示"调用此代码"。
可以这样想:在第一个代码示例中,每次呈现component
时,都会调用renderResults
。每次发生这种情况时,setAllResultsVisible(!allResultsVisible)
,而不是等待点击,被调用。由于 React 按照自己的时间表执行渲染,因此不知道会发生多少次。
来自 React 文档:
使用 JSX,您可以传递函数作为事件处理程序,而不是字符串。
反应处理事件文档
注意:在沙盒中运行第一个代码示例时,我无法收到此确切的错误消息。我的错误是指无限循环。也许更新版本的 React 会产生所描述的错误?
您可以简单地更改 onlick 事件 添加() =>
在 setAllResultsVisible 之前
<p onClick={() => setAllResultsVisible(!allResultsVisible) }>
More results v
</p>
它将完美运行
即使在上述修复之后,此错误也有一些其他原因。我在下面写一个为我发生的用例。
function Comp(props){return <div>{props.val}</div>}
在 jsx 中可以通过以下方式调用此组件:
1. <Comp val={3} /> // works well
2. { Comp({val:3}) } // throws uncaught invariant violation error, at least it throw in my case, may be there were other factors, but changing back to first way removed that problem
看到问题可以是 React :
渲染- 的钩子比之前的渲染少。 渲染
- 的钩子比之前的渲染更多。
在这两种情况下,事情都可能像你有一个条件语句调用相同的函数,该函数从不同的地方返回渲染,就像两者都包装在父返回函数中一样:
const parentFunc = () => {
if(case==1)
return function_a();
if (case==2)
return function_b();
}
现在 function_a() 可以是一个创建两个或一个钩子的函数,假设 useStyle() 或其他任何东西
function_b() 可以是一个不创建钩子的函数。
现在,当 parentFunc 返回 function_a() 渲染一个钩子而 function_b() 渲染没有钩子时,react 会告诉你,从同一个渲染函数返回了两个不同的渲染,一个有两个或一个钩子,另一个有一个钩子,这种差异会导致错误。错误是
渲染的钩子更少。而且错误很明显。
当情况被颠倒并且返回 function_b() 是条件的第一个原因时,反应会告诉你,从同一个渲染函数返回了不同的渲染,错误将是。
渲染的钩子比以前的渲染更多。
现在,解决方案:
更改代码流,例如创建 function_ab(),这将确保渲染所有正在使用的钩子并在该函数中:
const function_ab = () => {
if(case==1)
return (<div></div>) //or whatever
if(case==2)
return (<div>I am 2 </div>) //or whatever
}
就我而言,我在 if 条件中以以下方式使用了 setState() 钩子,所以在那之后我遇到了一个错误,我已经解决了。根据反应钩子文档,如果条件,我们不应该在里面使用钩子。
错误:
import React, { useState, useCallback } from 'react';
import './style.css';
export default function App() {
const [count, setCount] = useState(0);
if(count < 10){
return (
<div>
<h1>Hello Count!</h1>
<button onClick={useCallback(setCount((count) => count + 1))}>
click to add
</button>
</div>
);
} else {
return <div>Count reached 10!</div>
}
}
溶液:
import React, { useState, useCallback } from 'react';
import './style.css';
export default function App() {
const [count, setCount] = useState(0);
const handleIncrement = useCallback(() => {
setCount((count) => count + 1)
})
if(count < 10){
return (
<div>
<h1>Hello Count!</h1>
<button onClick={handleIncrement}>click to add</button>
</div>
);
} else {
return <div>Count reached 10!</div>
}
}
我认为这可以归类为错误,但很好奇地了解开发人员对此的看法。基本上,当从 0 个钩子过渡到正数钩子时,渲染的钩子比上一个渲染错误期间更多的钩子似乎不会被抛出。
例如,当组件从渲染 1 个钩子变为 2 个钩子时,确实会引发错误。
反应版本:17.0.2
问题出在调用setAllResultsVisible
的onClick
内,它将触发状态更改并在每次渲染时产生结果
onClick={ setAllResultsVisible(!allResultsVisible) }
将其更改为函数调用:
onClick={_ => setAllResultsVisible(!allResultsVisible) }
在组件中返回之前,您必须使用钩子