为什么组件会被重复渲染两次?

5

这是我的 App.js 代码:

function App() {
  console.log('Rendering the App  component');
  const [someBooleanVar, updateBooleanVar] = React.useState(false);

    console.log(someBooleanVar);

  const clickHandler = () => {
    console.log('In clickHandler');
    console.log(someBooleanVar);
    updateBooleanVar(true);
  };

  return (
    <div className="app">
      <h1>Test App</h1>
      <button onClick={clickHandler}>Toggle</button>
    </div>
  );
}
    
ReactDOM.render(<App />, document.querySelector('#app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="app"></div>

当页面第一次加载时,我在控制台上看到以下内容:

Rendering the App  component
App.js:12 false

现在,当我点击按钮时,控制台显示:

In clickHandler
App.js:16 false
App.js:8 Rendering the App  component
App.js:12 true

如果我第二次点击按钮,控制台会输出:
In clickHandler
App.js:16 true
App.js:8 Rendering the App  component
App.js:12 true

在随后的点击中,这些行会被打印出来:
In clickHandler
App.js:16 true

我的问题是:状态在第一次单击时就从 false 更新为 true。那么,为什么第二次单击会导致组件重新呈现,即使状态没有改变?

我刚刚测试了您的代码,并添加了一个useEffect钩子,每当someBooleanVar更新时就执行它; useEffect钩子仅在初始呈现后和第一次单击后执行两次:这表明状态确实只更新了一次,但是_为什么组件正在重新呈现?_ - Yousaf
通常情况下,您不需要关心渲染函数被调用的次数,因为它们本来就应该是幂等的。 - AKX
@AKX 我同意你的观点,但我试图了解memo对React性能的影响,这就是我偶然发现这个问题的原因。 - Shariq Hasan Khan
@shariqkhan,你可能需要重新措辞问题,将“三次”改为“两次”,这样人们才能找到它。 - Aakash Verma
1
@AKX 我不同意。如果组件实现不完美,可能会有很多泄漏 - 子组件可能没有被记忆化,昂贵的计算可能没有被记忆化,布局动画或调整可能是昂贵的,只是其中几个例子。 - Aakash Verma
@AakashVerma 当然可以,但你可以使用浏览器(或React Dev Tools)的性能工具来找到它们。 - AKX
1个回答

2
从 Github 问题 中找到 这个 评论

这是由于 React 并发实现细节的已知问题。我们不知道当前提交的两个版本哪一个更便宜。当出现这种歧义时,我们必须再次渲染一次,然后我们知道两个版本都是相同的,这就无关紧要了。

因此,我们不能帮助涉及的组件,但我们可以帮助子组件不受影响。

怎么做?

使用 React.memo

因此,组件的子元素不会受到第二个无用的重新渲染的影响。


哦,那么这是一个已知问题。听到这个消息有点放心。一开始我对 React 的理解就不是很多,所以一直在自问为什么会发生这种情况,并尝试为自己提供合理的解释。现在知道这是一个已知的 bug,我可以继续前进了。 - Shariq Hasan Khan
这是一个实际情况,我会称之为“缺陷即特性”的案例:)你想要速度还是想要系统完美无缺地运行。让我更新我的答案来帮助你。 - Aakash Verma
@Akash Verma 不确定这是一个特性。它会多次不必要地重新评估组件,既不完美也不快速。 - Shariq Hasan Khan
@shariqkhan 请再次阅读对该问题的评论。这是一个功能,如果可能的话,可以帮助避免运行复杂的并发解决算法,代价是重新渲染组件一次,这更便宜。 - Aakash Verma
我不得不在这里与你持不同意见。他们明确表示这是一个怪异现象。 如果它是一个特性,它就会在文档中说明,并且不会让人们想知道为什么它的行为方式是这样的。 评论提到他们不知道如何廉价地计算并发事物,所以他们使用了这个解决方法。但是你的答案帮助我理解了这不是我的代码引起的问题,所以谢谢你。 - Shariq Hasan Khan
@shariqkhan 是的,我同意将其称为特殊功能比将其称为功能更好,但这不是一个错误-他们非常清楚这一点,并且没有修复它。我希望你不是软件开发领域的新手,因为你会在多个未记录的小功能上绊倒,你将通过阅读源代码来找到它们 - 文档是人们编写的,所以它不可能是完美的。无论如何,我很高兴我的答案能够帮助到你。 - Aakash Verma

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接