【问题标题】:Uncaught Invariant Violation: Rendered more hooks than during the previous renderUncaught Invariant Violation:渲染的钩子比上一次渲染时更多
【发布时间】: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 函数吗?最好能解释一下修复工作的原因。

【问题讨论】:

    标签: javascript reactjs react-hooks


    【解决方案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:

          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
          }
          

          【讨论】:

            【解决方案6】:

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

            onClick={ setAllResultsVisible(!allResultsVisible) }
            

            改为函数调用:

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

            【讨论】:

              猜你喜欢
              • 2019-12-24
              • 2021-12-01
              • 2021-05-31
              • 2020-11-26
              • 1970-01-01
              • 2021-10-12
              • 2021-01-06
              • 2021-02-09
              • 2019-10-02
              相关资源
              最近更新 更多