未捕获的不变违规:比上一次渲染期间渲染的钩子更多

时间:2019-04-10 23:45:04

标签: javascript reactjs react-hooks

我有一个看起来像这样的组件(非常简化的版本):

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>;
}

我不再遇到该错误。是因为我在setState返回的jsx中包含了renderResults函数吗?解释一下该修复程序为何起作用,将是很棒的事情。

6 个答案:

答案 0 :(得分:7)

我遇到了同样的问题。我正在做的事情是这样的:

const Table = (listings) => {
    
    const {isLoading} = useSelector(state => state.tableReducer);
        
    useEffect(() => {
        console.log("Run something")
    }, [])
    
    if(isLoading){
        return <h1>Loading...</h1>
    }
    return (<table>{listings}</table>)
}

我认为发生的情况是在第一次渲染时,组件提前返回并且 useEffect 没有运行。当 isLoading 状态改变时,useEffect 运行,我得到错误 - 钩子渲染的次数比之前的渲染次数多。

一个简单的改变修复了它:

fd.c

答案 1 :(得分:2)

该修复程序起作用是因为第一个代码示例(出错的代码)调用onClick内部的函数,而第二个示例(工作的示例)将函数传递给onClick。区别在于那些非常重要的括号,在JavaScript中意味着“调用此代码”。

这样想:在第一个代码示例中,每次渲染component时,都会调用renderResults。每次发生时,都会调用setAllResultsVisible(!allResultsVisible)而不是等待单击。由于React按照自己的时间表执行渲染,因此无法确定将发生多少次。

从React文档中:

  

使用JSX,您可以传递一个函数作为事件处理程序,而不是字符串。

Bitcoin Core BIP39 Specifications

注意:在沙箱中运行第一个代码示例时,我无法获得此确切的错误消息。我的错误是指无限循环。也许是更新版本的React产生了所描述的错误?

答案 2 :(得分:2)

您只需在setAllResultsVisible之前添加() =>即可更改onlick事件

<p onClick={() => setAllResultsVisible(!allResultsVisible) }> 
    More results v
</p>

它将完美运行

答案 3 :(得分:2)

即使在完成上述修复后,此错误也有其他一些原因。我在下面写一个针对我的用例。

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

答案 4 :(得分:0)

看到问题可以是React:

  1. 渲染的钩子比以前的渲染少。
  2. 与以前的渲染相比,渲染了更多的钩子。

在两种情况下,事情都可能像您有一条条件语句调用同一函数一样,该函数从不同的位置返回渲染,就像都包裹在父返回函数中一样:

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
}

答案 5 :(得分:0)

问题发生在onClick的调用setAllResultsVisible中,它将触发状态更改并在每次渲染时产生结果

onClick={ setAllResultsVisible(!allResultsVisible) }

将其更改为函数调用:

onClick={_ => setAllResultsVisible(!allResultsVisible) }