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

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

作者:Alex Sidorenko

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

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

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

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

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

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

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

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

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

  1. 在重新渲染的时候,会进行比较,看两次是不是相同的
  1. 然后比较 props 的时候,会发现不同,所以需要重新渲染。

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

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

**但是为什么传递的 props 在重新渲染之间保留标识呢? **

在上面的例子中,children 属性来自 Resizer 的父组件。当我们触发 Resizer 的状态更新时,Resizer 会重新渲染,但其父组件不会。

因此, 元素不会被重新创建,保持了在 Resizer 重新渲染之间的引用标识。请注意,如果更新父组件的状态,所有内容都将重新渲染。

这种优化不仅适用于 children,而且适用于任何 props,比如下面的这个例子。

js 复制代码
// 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)
相关推荐
就是帅我不改几秒前
揭秘Netty高性能HTTP客户端:NIO编程的艺术与实践
后端·面试·github
墨渊君1 小时前
“蒙”出花样!用 CSS Mask 实现丝滑视觉魔法
前端·css
isysc11 小时前
面了一个校招生,竟然说我是老古董
java·后端·面试
uhakadotcom2 小时前
静态代码检测技术入门:Python 的 Tree-sitter 技术详解与示例教程
后端·面试·github
huabuyu2 小时前
基于 React + MarkdownIt 的 Markdown 渲染器实践:支持地图标签和长按复制
前端
芦苇Z2 小时前
HTML <a> 标签的 rel 属性全解析:安全、隐私与 SEO 最佳实践
前端·html
在这儿不行2 小时前
Android 15边到边模式
前端
源猿人2 小时前
企业级文件浏览系统的Vue实现:架构设计与最佳实践
前端·javascript·数据可视化
红红大虾2 小时前
Defold引擎中关于CollectionProxy的使用
前端·游戏开发