React 递归重新渲染子组件,但存在一个微妙之处。

原文链接: alexsidorenko.com/blog/react-…

作者:Alex Sidorenko

本篇文章主要聊聊 React 渲染子组件的时候,是否会产生重复渲染的问题,以及为什么会产生这种问题,还有如何规避的一些思考。

我们首先来看一个现象,如果说,这里直接用 Hello,而不用子组件,那么 Resizer 和 Hello 都会被重新渲染。

React 递归重新渲染子组件,但存在一个微妙之处。

但如果你传入了 children,这里就智慧重新渲染 resizer。

React 递归重新渲染子组件,但存在一个微妙之处。

等一下… React 在状态更新时会递归重新渲染子组件吗?

是的 – 看看这个。然而,重新渲染并不等于 DOM 更新。React 使用协调算法(reconciliation)仅更新 DOM 中所需的最小部分。

那么,为什么 React 不重新渲染 children 属性呢?

React 递归地重新渲染,当它遇到能够保留与上一次渲染相同引用标识的元素时,就可以停止递归。

传递给组件的 props 在组件状态更新时保留其标识。这就是为什么以这种方式组织组件可以作为记忆化的替代方案。

我们可以先来看看,如果不用子组件,直接用 Hello 组件,会有什么效果。

  1. 在重新渲染的时候,会进行比较,看两次是不是相同的

React 递归重新渲染子组件,但存在一个微妙之处。

  1. 然后比较 props 的时候,会发现不同,所以需要重新渲染。

React 递归重新渲染子组件,但存在一个微妙之处。

那如果是使用子组件的情况呢?

React 递归重新渲染子组件,但存在一个微妙之处。

那么会判断它两个是相等的,然后不进行重新渲染。

**但是为什么传递的 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链接

将包含解决方案的链接作为回复发送给这条推文。

相关文章:

  1. 《在你使用 memo 之前》(作者:Dan Abramov)
  2. 《优化 React 重新渲染的一个简单技巧》(作者:Kent C. Dodds)
  3. 《React 渲染行为的(基本上是)完整指南》(作者:Mark Erikson)

原文链接:https://juejin.cn/post/7313242038560079898 作者:阳树阳树

(0)
上一篇 2023年12月17日 上午11:13
下一篇 2023年12月18日 上午10:00

相关推荐

发表回复

登录后才能评论