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



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

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函数吗?最好能解释为什么修复程序有效。

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

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

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

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

来自 React 文档:

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

反应处理事件文档

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

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

<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

看到问题可以是 React :

渲染
  1. 的钩子比之前的渲染少。
  2. 渲染
  3. 的钩子比之前的渲染更多。

在这两种情况下,事情都可能像你有一个条件语句调用相同的函数,该函数从不同的地方返回渲染,就像两者都包装在父返回函数中一样:

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() 是条件的第一个原因时,反应会告诉你,从同一个渲染函数返回了不同的渲染,错误将是。

渲染

的钩子比以前的渲染更多。

现在,解决方案:

更改代码流,例如创建 function_ab(),这将确保渲染所有正在使用的钩子并在该函数中:

const function_ab = () => {
if(case==1)
return (<div></div>) //or whatever
if(case==2)
return (<div>I am 2 </div>) //or whatever
}

就我而言,我在 if 条件中以以下方式使用了 setState() 钩子,所以在那之后我遇到了一个错误,我已经解决了。根据反应钩子文档,如果条件,我们不应该在里面使用钩子。

错误:

import React, { useState, useCallback } from 'react';
import './style.css';
export default function App() {
const [count, setCount] = useState(0);
if(count < 10){
return (
<div>
<h1>Hello Count!</h1>
<button onClick={useCallback(setCount((count) => count + 1))}>
click to add
</button>
</div>
);
} else {
return <div>Count reached 10!</div>
}
}

溶液:

import React, { useState, useCallback } from 'react';
import './style.css';
export default function App() {
const [count, setCount] = useState(0);
const handleIncrement = useCallback(() => {
setCount((count) => count + 1)
})

if(count < 10){
return (
<div>
<h1>Hello Count!</h1>
<button onClick={handleIncrement}>click to add</button>
</div>
);
} else {
return <div>Count reached 10!</div>
}
}

我认为这可以归类为错误,但很好奇地了解开发人员对此的看法。基本上,当从 0 个钩子过渡到正数钩子时,渲染的钩子比上一个渲染错误期间更多的钩子似乎不会被抛出。

例如,当组件从渲染 1 个钩子变为 2 个钩子时,确实会引发错误。

反应版本:17.0.2

问题出在调用setAllResultsVisibleonClick内,它将触发状态更改并在每次渲染时产生结果

onClick={ setAllResultsVisible(!allResultsVisible) }

将其更改为函数调用:

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

在组件中返回之前,您必须使用钩子

相关内容

  • 没有找到相关文章

最新更新