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 中混淆 () =&gt; 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 事件添加 () =&gt;

<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:渲染的钩子比上一次渲染时更多的主要内容,如果未能解决你的问题,请参考以下文章

react返回数组问题

mongo源码学习invariant

将 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

Distinctive Image Features from Scale-Invariant