Uncaught Invariant Violation:渲染的钩子比上一次渲染时更多
Posted
技术标签:
【中文标题】Uncaught Invariant Violation:渲染的钩子比上一次渲染时更多【英文标题】:Uncaught Invariant Violation: Rendered more hooks than during the previous render 【发布时间】:2019-09-01 12:22:05 【问题描述】:我有一个看起来像这样的组件(非常简化的版本):
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
函数吗?最好能解释一下修复工作的原因。
【问题讨论】:
【参考方案1】:修复之所以有效,是因为第一个代码示例(出错的)调用了 onClick
内的一个函数,而第二个(工作的)将一个函数传递给了 onClick
。不同之处在于那些非常重要的括号,在 javascript 中表示“调用此代码”。
这样想:在第一个代码示例中,每次呈现component
时,都会调用renderResults
。每次发生这种情况时,都会调用setAllResultsVisible(!allResultsVisible)
,而不是等待点击。由于 React 按自己的时间表执行渲染,因此不知道会发生多少次。
来自 React 文档:
使用 JSX,您可以将函数作为事件处理程序传递,而不是字符串。
React Handling Events Docs
注意:在沙盒中运行第一个代码示例时,我无法得到这个确切的错误消息。我的错误是指无限循环。也许更新版本的 React 会产生所描述的错误?
【讨论】:
对,我犯了一个严重的错误。我总是在 onClicks 中混淆() => function()
和 function()
。【参考方案2】:
我遇到了同样的问题。我正在做的事情是这样的:
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>)
【讨论】:
【参考方案3】:您可以在 setAllResultsVisible 之前更改您的 onlick 事件添加 () =>
<p onClick=() => setAllResultsVisible(!allResultsVisible) >
More results v
</p>
它会完美运行
【讨论】:
【参考方案4】:即使经过上述修复,此错误还有其他一些原因。我正在写下面一个发生在我身上的用例。
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
【讨论】:
【参考方案5】:看问题可以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() 返回条件的第一个原因时,react 会告诉你从同一个渲染函数返回不同的渲染并且错误将是。
比之前的渲染渲染了更多的钩子。
现在,解决方案:
更改代码流,例如创建 function_ab(),这将确保所有正在使用的钩子都被渲染并在该函数中:
const function_ab = () =>
if(case==1)
return (<div></div>) //or whatever
if(case==2)
return (<div>I am 2 </div>) //or whatever
【讨论】:
【参考方案6】:问题在onClick
内,因为setAllResultsVisible
被调用,它将触发状态更改并在每次渲染时产生结果
onClick= setAllResultsVisible(!allResultsVisible)
改为函数调用:
onClick=_ => setAllResultsVisible(!allResultsVisible)
【讨论】:
以上是关于Uncaught Invariant Violation:渲染的钩子比上一次渲染时更多的主要内容,如果未能解决你的问题,请参考以下文章
将 Apollo Angular 2 用于 Angular ts-invariant/lib/invariant.d.ts:7:78 时出错
Invariant Violation:Invariant Violation:元素类型无效:预期为字符串(对于内置组件)但得到:未定义
Mono / XBuild Invariant Language (Invariant Culture) vs. VS2015 MSBuild 的 Neutral Language