• 《React扩展知识二》PureComponent / render props / Error boundary /组件通信方式总结


    前言

    博主主页👉🏻蜡笔雏田学代码
    专栏链接👉🏻React专栏
    接上一篇文章react的扩展知识,今天来学习React的另外一些扩展知识
    感兴趣的小伙伴一起来看看吧~🤞

    在这里插入图片描述

    1. 组件优化

    Component的2个问题

    问题1: 只要执行setState(),即使不改变状态数据 ( this.setState({})),组件也会重新render() ==> 效率低

    问题2: 只要当前组件重新render(),就会自动重新render子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低

    效率高的做法

    只有当组件的state或props数据发生改变时才重新render()

    原因

    Component中的shouldComponentUpdate()总是返回true

    重写shouldComponentUpdate()方法

    问题1: 只要执行setState(),即使不改变状态数据 ( this.setState({})),组件也会重新render() ==> 效率低

    比较新旧state或props数据,如果有变化才返回true,如果没有返回false

    export default class Parent extends Component {
    
      state = { carName: '奔驰' }
    
      changeCar = () => {
        this.setState({})
      }
    
      shouldComponentUpdate(nextProps, nextState) {
        console.log(this.props, this.state)  //目前的props和state
        console.log(nextProps, nextState) //接下来要变化的目标props,目标state
        if (this.state.carName === nextState.carName) return false
        else return true
      }
    
      render() {
        console.log('parent--render')
        return (
          <div className='parent'>
            <h3>我是Parent组件</h3>
            <span>我的车名字是:{this.state.carName}</span><br />
            <button onClick={this.changeCar}>点我换车</button>
          </div>
        )
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    效果
    在这里插入图片描述

    此时在点击完按钮之后,当前组件的state状态并没有发生变化,所以shouldComponentUpdate()里的if判断返回的是false,进而不会引起组件的重新渲染,不会调用render。

    问题2和问题1一样重写shouldComponentUpdate()方法

    问题2: 只要当前组件重新render(),就会自动重新render子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低

    import React, { Component } from 'react'
    import './index.css'
    
    export default class Parent extends Component {
      state = { carName: '奔驰' }
    
      changeCar = () => {
        this.setState({ carName: '宝马' })
      }
    
      shouldComponentUpdate(nextProps, nextState) {
        // console.log(this.props, this.state)  //目前的props和state
        // console.log(nextProps, nextState) //接下来要变化的目标props,目标state
        if (this.state.carName === nextState.carName) return false
        else return true
      }
    
      render() {
        console.log('parent--render')
        return (
          <div className='parent'>
            <h3>我是Parent组件</h3>
            <span>我的车名字是:{this.state.carName}</span><br />
            <button onClick={this.changeCar}>点我换车</button>
            <Child carName='奥拓' />
          </div>
        )
      }
    }
    
    class Child extends Component {
      shouldComponentUpdate(nextProps, nextState) {
        console.log(this.props, this.state)  //目前的props和state
        console.log(nextProps, nextState) //接下来要变化的目标props,目标state
        if (this.props.carName === nextProps.carName) return false
        else return true
      }
    
      render() {
        console.log('child--render')
        return (
          <div className='child'>
            <h3>我是Child组件</h3>
            <span>我接到的车是:{this.props.carName}</span>
          </div>
        )
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    效果
    在这里插入图片描述

    此时子组件接收到的props数据是固定不变的 ( < < <Child carName=‘奥拓’ />) ,所以子组件里的shouldComponentUpdate()里的if判断返回的是false,进而不会引起子组件的重新渲染,不会调用render。

    使用PureComponent

    当state状态里有多组数据,那么shouldComponentUpdate()里就要进行多个判断,这样显然在真正开发里是不可行的。所以我们可以不用手动写shouldComponentUpdate()方法, 使用PureComponent, 可以重写shouldComponentUpdate()里的逻辑, 只有state或props数据有变化才返回true。(项目中一般使用PureComponent来优化)

    import React, { PureComponent } from 'react'
    ...
    export default class Parent extends PureComponent{}
    ...
    class Child extends PureComponent{}
    ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注意! 🔥 PureComponent只是进行state和props数据的浅比较,如果只是数据对象内部数据变了,返回false。 不要直接修改state数据, 而是要产生新数据

    //错误写法
    const obj = this.state
    obj.carName = '宝马'
    this.setState(obj) 
    
    • 1
    • 2
    • 3
    • 4

    这样修改数据,只是对原state对象内的值做了修改,但是对象的引用地址没变!!
    在PureComponent看来,引用地址没变时,组件内部的shouldComponentUpdate返回false,也就不会重新render,数据更新就失败了。

    //正确写法
    this.setState({ carName: '宝马' })
    
    • 1
    • 2

    这样修改数据,就是用一个新对象{ carName: ‘宝马’ }替换了原来的state对象,数据的引用地址变化了,那么PureComponent组件内部的shouldComponentUpdate返回true,组件重新render,数据更新成功。

    2. render props

    如何向组件内部动态传入带内容的结构(标签)?

    Vue中:

    使用slot插槽技术,也就是通过组件标签体传入结构

    <A>
      <B/>
    </A>
    
    • 1
    • 2
    • 3

    React中:

    使用children props: 通过组件标签体传入结构

    使用render props: 通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性

    children props

    <A>
      <B>xxxx</B>
    </A>
    {this.props.children}
    问题: 如果B组件需要A组件内的数据, ==> 做不到 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    export default class Parent extends Component {
      render() {
        return (
          <div className='parent'>
            <h3>我是parent组件</h3>
            <A>
              <B />
            </A>
          </div>
        )
      }
    }
    
    class A extends Component {
      render() {
        console.log(this.props)
        return (
          <div className='a'>
            <h3>我是A组件</h3>
            {this.props.children}
          </div>
        )
      }
    }
    
    class B extends Component {
      render() {
        return (
          <div className='b'>
            <h3>我是B组件</h3>
          </div>
        )
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    效果

    在这里插入图片描述

    render props

    <A render={(data) => <C data={data}></C>}></A>
    A组件: {this.props.render(内部state数据)}
    C组件: 读取A组件传入的数据显示 {this.props.data} 
    
    • 1
    • 2
    • 3

    如果在children props写法的基础上, 若要将A组件state身上的name属性传给B组件, 我们就需要使用render props来写:

    export default class Parent extends Component {
      render() {
        return (
          <div className='parent'>
            <h3>我是parent组件</h3>
            <A render={(name) => <B name={name} />} />
          </div>
        )
      }
    }
    
    class A extends Component {
      state = { name: 'jack' }
      render() {
        console.log(this.props)
        const { name } = this.state
        return (
          <div className='a'>
            <h3>我是A组件</h3>
            {this.props.render(name)}
          </div>
        )
      }
    }
    
    class B extends Component {
      render() {
        return (
          <div className='b'>
            <h3>我是B组件,{this.props.name}</h3>
          </div>
        )
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    效果

    在这里插入图片描述

    3. 错误边界Error boundary

    理解

    错误边界(Error boundary):把错误控制在一定范围内,比如后端返回来的数据类型出错,或者一些其他的错误,我们可以在界面中提示用户一些“网络繁忙请稍后重试”信息,错误边界Error boundary就是用来捕获后代组件错误,渲染出备用页面。

    错误边界一般是在容易发生错误的组件的父组件中处理。

    特点

    只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误。

    使用方式

    getDerivedStateFromError配合componentDidCatch

    export default class Parent extends Component {
      state = {
        hasError: '' //用于标识子组件是否产生错误
      }
    
      // 如果Parent组件的子组件出现了任何的报错,都会调用这个钩子函数,调用的时候传入error错误信息
      static getDerivedStateFromError(error) {
        console.log(error)
        return { hasError: error }// 返回新的state
      }
    
      render() {
        return (
          <div>
            <h2>我是Parent组件</h2>
            {this.state.hasError ? <h2>当前网络不稳定,请稍后再试</h2> : <Child />}
          </div>
        )
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    // 如果组件在渲染的整个过程中,由于子组件出现了问题,引发一些错误,
    //就会调用此函数,用来统计错误,反馈给服务器,用于通知编码人员进行bug的解决
    componentDidCatch(error, info) {
      // 统计页面的错误。发送请求发送到后台去
        console.log(error, info);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    效果

    在这里插入图片描述

    4. 组件通信方式总结

    组件间的关系

    • 父子组件
    • 兄弟组件(非嵌套组件)
    • 祖孙组件(跨级组件)

    几种通信方式

    1. props:
      • children props
      • render props
    2. 消息订阅-发布:
      • pubs-sub、event等等
    3. 集中式状态管理:
      • redux、dva等等
    4. conText:
      • 生产者-消费者模式

    比较好的搭配方式

    父子组件:props
    兄弟组件:消息订阅-发布、集中式管理
    祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(开发用的少,封装插件用的多)

    今天的分享就到这里啦✨ \textcolor{red}{今天的分享就到这里啦✨} 今天的分享就到这里啦

    原创不易,还希望各位大佬支持一下 \textcolor{blue}{原创不易,还希望各位大佬支持一下} 原创不易,还希望各位大佬支持一下

    🤞 点赞,你的认可是我创作的动力! \textcolor{green}{点赞,你的认可是我创作的动力!} 点赞,你的认可是我创作的动力!

    ⭐️ 收藏,你的青睐是我努力的方向! \textcolor{green}{收藏,你的青睐是我努力的方向!} 收藏,你的青睐是我努力的方向!

    ✏️ 评论,你的意见是我进步的财富! \textcolor{green}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!

  • 相关阅读:
    leetcode148. 排序链表
    cv::Mat 的常见操作方法
    记录使用Docker Compose 部署《XAPI项目》遇道的问题及解决方案
    基于SpringBoot的在线聊天室系统,源码,数据库脚本,项目导入运行视频教程,论文撰写教程
    安装 torch_geometric
    九章云极DataCanvas大模型系列成果发布会重磅来袭,诚邀见证!
    软件开发介绍
    Idea中使用测试类,提示无可任务No tasks available
    linux系统时钟控制
    GDAL获取Hadoop hdfs tif文件信息(java)
  • 原文地址:https://blog.csdn.net/xuxuii/article/details/126255974