原文链接: alexsidorenko.com/blog/react-…
作者:Alex Sidorenko
本篇文章主要聊聊 React 渲染子组件的时候,是否会产生重复渲染的问题,以及为什么会产生这种问题,还有如何规避的一些思考。
我们首先来看一个现象,如果说,这里直接用 Hello,而不用子组件,那么 Resizer 和 Hello 都会被重新渲染。
但如果你传入了 children,这里就智慧重新渲染 resizer。
等一下… React 在状态更新时会递归重新渲染子组件吗?
是的 – 看看这个。然而,重新渲染并不等于 DOM 更新。React 使用协调算法(reconciliation)仅更新 DOM 中所需的最小部分。
那么,为什么 React 不重新渲染 children 属性呢?
React 递归地重新渲染,当它遇到能够保留与上一次渲染相同引用标识的元素时,就可以停止递归。
传递给组件的 props 在组件状态更新时保留其标识。这就是为什么以这种方式组织组件可以作为记忆化的替代方案。
我们可以先来看看,如果不用子组件,直接用 Hello 组件,会有什么效果。
- 在重新渲染的时候,会进行比较,看两次是不是相同的
- 然后比较 props 的时候,会发现不同,所以需要重新渲染。
那如果是使用子组件的情况呢?
那么会判断它两个是相等的,然后不进行重新渲染。
**但是为什么传递的 props 在重新渲染之间保留标识呢? **
在上面的例子中,children 属性来自 Resizer 的父组件。当我们触发 Resizer 的状态更新时,Resizer 会重新渲染,但其父组件不会。
因此, 元素不会被重新创建,保持了在 Resizer 重新渲染之间的引用标识。请注意,如果更新父组件的状态,所有内容都将重新渲染。
这种优化不仅适用于 children,而且适用于任何 props,比如下面的这个例子。
// When the state of "Resizer" updates
//"children" and "handle" won't re-render
function Resizer({ children, handle }) {
let [x, updateX] = useState(0)
return (
<div style={{ width: x }}>
{children}
<span>{handle}</span>
</div>
)
}
我如何在实践中使用它?
这个功能与状态共置技术紧密相连。
通过以一种方式组织组件,将状态与更新此状态的 UI 部分隔离,它自然而然地实现了对 React 应用的优化
在没有使用 refs 和 memo 的情况下优化 React 性能。
不要将本指南作为过分分析重新渲染的理由,因为这是事倍功半的。
编码挑战:LongList 组件在每次鼠标移动时重新渲染,导致性能问题。
你能否通过应用本文中的技术来改善应用的性能?CodeSandbox链接。
将包含解决方案的链接作为回复发送给这条推文。
相关文章:
- 《在你使用 memo 之前》(作者:Dan Abramov)
- 《优化 React 重新渲染的一个简单技巧》(作者:Kent C. Dodds)
- 《React 渲染行为的(基本上是)完整指南》(作者:Mark Erikson)
原文链接:https://juejin.cn/post/7313242038560079898 作者:阳树阳树