react组件多次渲染问题的解决

 更新时间:2023年07月20日 10:21:44   作者:Ruonorth  
本文主要介绍了react组件多次渲染问题的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

问题背景

在数据没有发生变化的情况下React组件会进行数次重复渲染,绘制出来完全相同的两个图

排查思路

寻找子组件重渲染原因实验

测试一:在子组件的props未发生任何变更的情况下是否会发生重新渲染

import React, { useState } from "react"
import { Select } from "antd"
const { Option } = Select
export function Chart() {
    console.log("父组件重新渲染了")
    const [selectedOption, setSelectedOption] = useState("option1")
    function handleSelectChange(value) {
        setSelectedOption(value)
    }
    return (
        <div>
            <Select defaultValue={selectedOption} onChange={handleSelectChange}>
                <Option value="option1">Option 1</Option>
                <Option value="option2">Option 2</Option>
                <Option value="option3">Option 3</Option>
            </Select>
            <ChildComponent />
        </div>
    )
}
function ChildComponent() {
    console.log("ChildComponent重新渲染了")
    return <div>Hello from ChildComponent</div>
}

测试结论:子组件即使不接收任何props当父组件重新渲染时子组件也会重新渲染

解决方案

使用React.memo,关于memo介绍如下:

包装一个组件memo以获得该组件的记忆版本。只要组件的 props 没有改变,当它的父组件重新渲染时,组件的这个记忆版本通常不会被重新渲染。

但 React 可能仍然会重新渲染它:memoization 是一种性能优化,而不是保证。

import React, { useState } from "react"
import { Select } from "antd"
const { Option } = Select
export function Chart() {
    console.log("父组件重新渲染了")
    const [selectedOption, setSelectedOption] = useState("option1")
    function handleSelectChange(value) {
        setSelectedOption(value)
    }
    return (
        <div>
            <Select defaultValue={selectedOption} onChange={handleSelectChange}>
                <Option value="option1">Option 1</Option>
                <Option value="option2">Option 2</Option>
                <Option value="option3">Option 3</Option>
            </Select>
            {/* <ChildComponent /> */}
            <MemoChild />
        </div>
    )
}
function ChildComponent() {
    console.log("ChildComponent重新渲染了")
    return <div>Hello from ChildComponent</div>
}
const MemoChild = React.memo(ChildComponent)

即使使用memo,但其仍然会在其接受的props发生变更时及其所在的上下文发生变更时重新渲染。

因此为了更好的使用memo,应当尽量减少props的变化。具体方案如下:

  • 当props为引用值时应当使用useMemo返回相同引用值
  • 使得 props 中接受最少的必要信息
  • 当props包含函数时,要么在组件外部声明它以使其永远不会更改,要么useCallback在重新渲染之间缓存其定义。

例如:当props为对象时,应当减少props对象的变化

测试内组件对memo的影响

接下来进行另外的测试,将子组件放在Adapt中称为内部组件

可以发现,此时子组件的缓存失效了,尽管子组件没有props的变化

但每次父组件重新渲染子组件仍然会重新渲染

import React, { useState } from "react"
import { Select } from "antd"
const { Option } = Select
export function Adapt() {
    console.log("父组件重新渲染了")
    const [selectedOption, setSelectedOption] = useState("option1")
    function handleSelectChange(value) {
        setSelectedOption(value)
    }
    return (
        <div>
            <Select defaultValue={selectedOption} onChange={handleSelectChange}>
                <Option value="option1">Option 1</Option>
                <Option value="option2">Option 2</Option>
                <Option value="option3">Option 3</Option>
            </Select>
            {/* <ChildComponent /> */}
            <MemoChild />
        </div>
    )
}
function ChildComponent() {
    console.log("ChildComponent重新渲染了")
    return <div>Hello from ChildComponent</div>
}
const MemoChild = React.memo(ChildComponent)

React.memo 失效的原因是因为将 ChildComponent 和 MemoChild 定义在了 Adapt 组件的内部。这意味着每当 Adapt 组件重新渲染时,ChildComponent 和 MemoChild 都会重新定义,导致了缓存失效。

测试props变更对memo的影响

改写测试用例,测试当父组件的props变更时,子组件是否重新渲染

import React, { useState } from "react"
import { Select } from "antd"
const { Option } = Select
export function Adapt() {
    console.log("Adapt重新渲染了")
    const [filterList, setFilterList] = useState(null)
    function onRefresh(value) {
        setFilterList(value)
    }
    return (
        <div>
            <Chart {...{ onRefresh, filterList }} />
        </div>
    )
}
function Chart({ filterList, onRefresh }) {
    console.log("Chart重新渲染了")
    return (
        <div>
            <Select value={filterList} onChange={onRefresh}>
                <Option value="option1">Option 1</Option>
                <Option value="option2">Option 2</Option>
                <Option value="option3">Option 3</Option>
            </Select>
            <MemoChild />
        </div>
    )
}
function ChildComponent() {
    console.log("ChildComponent重新渲染了")
    return <div>hello</div>
}
const MemoChild = React.memo(ChildComponent)
export default Adapt

子组件仍然可以进行正常的缓存,可得知当子组件使用memo

无论是父组件的state或者props状态变更,只要子组件的props和上下文未变化都不会影响到子组件

问题结论

导致组件重复渲染的可能如下:

  • 当子组件未使用memo时,子组件会随着父组件一起重新渲染,因此即使子组件的props未变化仍会多次渲染子组件
  • 当子组件使用了memo,但仍然重新渲染
    • 子组件接受的props或者context变更了
    • 子组件的上下文及props都没变更,但子组件时内部组件所以每次都是重新创建的

因此为了优化子组件的多次渲染应当注意:

  • 重复的渲染可以使用memo缓存
  • 父组件传递的重复的props参数可以使用useMemo
  • 每次传递的props应当是最小的props,不然无关的props变更会影响组件缓存失效
  • 组件及函数应该尽量以纯函数形式写在组件之外,避免组件重渲染时重新创建函数带来的影响

 到此这篇关于react组件多次渲染问题的解决的文章就介绍到这了,更多相关react组件多次渲染内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • React全局状态管理的三种底层机制探究

    React全局状态管理的三种底层机制探究

    近两年前端技术的发展如火如荼,大量的前端项目都在使用或转向 Vue 和 React 的阵营,由前端渲染页面的单页应用占比也越来越高,这篇文章主要给大家介绍了关于React全局状态管理的三种底层机制,需要的朋友可以参考下
    2021-09-09
  • react中useRef的应用使用详解

    react中useRef的应用使用详解

    这篇文章主要介绍了react中useRef的应用使用详解的相关资料,需要的朋友可以参考下
    2023-05-05
  • 更强大的React 状态管理库Zustand使用详解

    更强大的React 状态管理库Zustand使用详解

    这篇文章主要为大家介绍了更强大的React 状态管理库Zustand使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • 深入理解React中何时使用箭头函数

    深入理解React中何时使用箭头函数

    对于刚学前端的大家来说,对于React中的事件监听写法有所疑问很正常,特别是React中箭头函数使用这块,下面这篇文章主要给大家深入的讲解了关于React中何时使用箭头函数的相关资料,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧。
    2017-08-08
  • react navigation中点击底部tab怎么传递参数

    react navigation中点击底部tab怎么传递参数

    本文主要介绍了react navigation中点击底部tab怎么传递参数,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • React tsx生成随机验证码

    React tsx生成随机验证码

    这篇文章主要为大家详细介绍了React tsx生成随机验证码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-04-04
  • react中使用swiper的具体方法

    react中使用swiper的具体方法

    本篇文章主要介绍了react中使用swiper的具体方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05
  • 在React项目中添加吸顶效果的代码示例

    在React项目中添加吸顶效果的代码示例

    在大型Web应用中,一个常见的设计需求是让某些组件具有吸顶效果,这意味着当页面向下滚动时,该组件会保持在屏幕顶部,在本文中,我们将介绍如何在React项目中实现吸顶效果。我们将首先讨论使用原生JavaScript领域的方法来实现,然后将这些方法与React结合起来
    2023-06-06
  • React diff算法原理详细分析

    React diff算法原理详细分析

    经典的diff算法中,将一棵树转为另一棵树的最低时间复杂度为 O(n^3),其中n为树种节点的个数。假如采用这种diff算法,一个应用有1000个节点的情况下,需要比较十亿次才能将dom树更新完成,显然这个性能是无法让人接受的
    2022-11-11
  • 解决React报错The tag is unrecognized in this browser

    解决React报错The tag is unrecognized in this browser

    这篇文章主要为大家介绍了解决React报错The tag is unrecognized in this browser示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12

最新评论