你用得到的 React 重新渲染的知识
前言
大家好,我是森木。至于为什么要写这篇文章,是因为在用 React 开发业务需求时由于不懂 React 的重新渲染机制而导致的问题;故此,在解决问题的同时也分析下到底什么情况下 React 会重新渲染、为什么 React 会重新渲染。当然,如果文中有和你有不同的看法,欢迎指出 👏
什么情况会触发 React 重新渲染
⚠️注意:forceUpdate() 方法也可以触发,但是有性能问题,所以这里先不做讲述
有一个事实需要知道,那就是 React 的重新渲染始于 state 的改变,这也是组件重新渲染的“触发器”。 emm...这句话听起来可能存在疑问,那就是子组件的重新渲染情况是怎样的? props 改变组件会不会影响?context 呢?🤔️
我们先讲问题简化,来看看 state 改变下,不做任何处理的组件的渲染是怎样的:
从例子中可以看出,每次更改
count
状态时,会影响到 Counter
组件和它的子组件 BigCountNumber
,证实了 state 改变重新渲染当前组件、 props 更改也会触发子组件的重新渲染但是另一个子组件 Description
也重新渲染了,而 App
并没有重新渲染,这也是符合 React 的自上而下的数据流的思想。
所以,到这里,我们有了一个初步的结论,那就是 state 的改变会触发组件以及它的子组件的渲染,但是,这时就有疑问了,如果按照理想情况下,应该是 props 改变了的组件才会重新渲染,怎么连没使用 state 与 props 的组件也重新渲染了呢?
是不是可以确定这样一个答案,React 很难 100% 确定 是否直接或间接地取决于计数状态变量。是的,在理想情况下,React 的组件都是“纯的”,纯的组件是给定对应 props 生成对应组件。但是,往往在现实生活中,人们总是能够写出一些“不纯的组件”,看下面的例子:
function CurrentTime() {
const now = new Date();
return (
<p>It is currently {now.toString()}</p>
);
}
该组件在渲染时将显示不同的值,因为它依赖于当前时间!🕙
该组件也没有依赖 props 值,但是他会依赖当前的时间,如果不进行重新渲染,是不是达不到目的? 这个问题某个版本与 refs 有关。如果我们将 ref 作为 prop 传递,React 将无法判断自上次渲染以来我们是否对它进行了更改。因此,为了安全起见,它选择重新渲染。 React 的一个目标是确保用户看到的 UI 与应用程序状态保持“同步”。因此,React 会在渲染过多时出错。它不想冒险向用户展示陈旧的 UI。
当一个组件重新渲染时,因为它的一个状态变量已经更新,重新渲染将沿着树向下级联,以便 React 填充这个新草图的细节,以捕获新的快照。
小结
这里的例子都围绕着 state 和 props 的改变,组件或者子组件是否重新渲染来讲解,并且通过看例子我们得出一个结论:state 变化会引起当前组件和它的所有子组件重新渲染,不管有没有 props 的改变。而且我们还分析出现这种情况的原因是因为 React 并不能 100% 确定我们哪些组件需要更新,或者是因为它认为这样做会牺牲很多东西,又或者他想把这种操作交给用户自己去优化等等,我们这里就不过多的揣测了,我们只要记住这种情况需要我们自己来优化就好啦~
如何避免不必要的重新渲染
我们前面提到过“纯组件”的概念,那我们可以试着把我们上面提到的例子改为“纯组件”,来告诉 React 这个组件确定是“纯组件”,看看是否能避免不必要的重新渲染:
function Description() {
return (
<p>
<span>仅做演示</span>
</p>
);
}
export default React.memo(Description);
使用 React.memo
或者 React.PureComponent
方法来忽略某些重新渲染。
废话不多说,直接上代码测试结果:
在例子中,我对 BigCountNumber
、CurrentTime
、Description
进行了包裹,发现 Description
和 CurrentTime
组件在状态改变下都不会重新渲染了,但是 BigCountNumber
还是会重新渲染。符合我们想要的效果 🥳
图示为下:
绿色为重新渲染了的组件,黄色边框代表为“纯组件”。
最后,Context 表现如何
我们还没有讨论过 Context
,但幸运的是,它并没有使这些事情变得太复杂。 默认情况下,如果组件的状态发生变化,组件的所有后代都将重新渲染。因此,如果我们通过 Context
向所有后代提供该状态,它并没有特别之处;无论哪种方式,这些组件都会重新渲染!
老规矩,还是上例子:
从例子中,我们看到组件被 React.memo
包裹,但是最终还是触发了重新渲染。
总结
最后,我们知道组件在状态改变、Context
改变和 props 改变都会引起重新渲染,前提是 React 知道组件是“纯组件”。如果不做任何处理的情况下,状态改变涉及到的所有组件树都会重新渲染。原因也很简单,就是 React 无法 100% 确定组件内部是否其他变化的依赖。
转载自:https://juejin.cn/post/7136851259557937159