Uncaught Invariant Violation:渲染的钩子比上一次渲染时更多

IT技术 javascript reactjs react-hooks
2021-05-16 11:00:14

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

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最好能解释一下此修复程序的工作原理。

4个回答

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

可以这样想:在第一个代码示例中,每次component渲染、renderResults调用。每次发生这种情况时setAllResultsVisible(!allResultsVisible),都会调用 ,而不是等待点击。由于 React 按照自己的时间表执行渲染,因此不知道会发生多少次。

来自 React 文档:

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

React 处理事件文档

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

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

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

您可以在 setAllResultsVisible() =>之前简单地更改您的 onlick 事件添加

<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