• 【React扩展】2、PureComponent、ErrorBoundary、render props和组件通信方式总结


    在这里插入图片描述

    欢迎来到我的博客
    📔博主是一名大学在读本科生,主要学习方向是前端。
    🍭目前已经更新了【Vue】、【React–从基础到实战】、【TypeScript】等等系列专栏
    🛠目前正在学习的是🔥 R e a c t 框架 React框架 React框架🔥,中间穿插了一些基础知识的回顾
    🌈博客主页👉codeMak1r.小新的博客

    本文被专栏【React–从基础到实战】收录

    🕹坚持创作✏️,一起学习📖,码出未来👨🏻‍💻!
    在这里插入图片描述

    1.组件优化PureComponent

    Component的2个问题

    1. 只要执行setState(),即使不改变状态数据,组件也会重新render();

      this.setState({});
      // 不改变状态,此时也会重新render()
      
      • 1
      • 2
    2. 只要当前组件重新render(),就会自动重新render子组件

      即使子组件没有用到父组件的任何数据 ==> 效率低;

    效率高的做法

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

    原因

    Component中的shouldComponentUpdate()总是返回true

    解决

    1. 重写shouldComponentUpdate()方法

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

    2. 使用PureComponent

      PureComponent重写了shouldComponentUpdate(),只有state或者props数据有变化才返回true

      注意⚠️:

      只是进行state和props数据的浅比较,如果只是数据对象内部数据变了,返回false;

      不要直接修改state数据,而是要产生新数据

    项目中一般使用PureComponent来优化。

    示例1:重写shouldComponentUpdate方法

    // 父组件this.setState({});但是没有修改数据,组件就不会render
    shouldComponentUpdate(nextProps, nextState) {
      console.log(this.props, this.state)  //目前的props和state
      console.log(nextProps, nextState)  // 要变化的目标props和state
      if (this.state.xxx=== nextState.xxx) return false
      else return true
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    判断状态更新前后的同一数据的值是否相同

    若相同,则shouldComponentUpdate返回false(不render)

    若不同,则shouldComponentUpdate返回true(允许render)

    示例2:PureComponent

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

    PureComponent内部重写了shouldComponentUpdate方法,不用我们自己手写。

    我们再来看前面提到的PureComponent存在的问题:

    PureComponent进行的是数据的浅比较,也就是说,在修改状态数据时,不可以

    const obj = this.state
    obj.carName = 'Porsche-911 GT3 RS'
    this.setState(obj)
    
    • 1
    • 2
    • 3

    这样修改数据,只是对原state对象内的值做了修改,但是对象的引用地址没变!!

    PureComponent看来,引用地址没变时,组件内部的shouldComponentUpdate返回false,也就不会重新render,数据更新就失败了。

    正确的更新状态应该是:

    this.setState({ carName: 'Porsche-911 GT3 RS' });
    
    • 1

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

    2.错误边界ErrorBoundary

    理解

    错误边界(ErrorBoundary):用来捕获后代组件错误,渲染出备用界面。

    所谓的边界,也就是把错误控制在一定范围内。比如后端的数据返回的是undefined或者返回的数据类型出现错误,又或者是其他的一些不可控的、未知的错误出现时,在页面上直接显示报错信息显然是不对的。这个时候,我们就应该把错误控制在可控范围内,一旦错误超出可控范围,我们直接在页面中“安抚”一下用户,可以提示用户:“网络繁忙请稍后重试”这样的信息,直接甩锅给运营商!(doge)

    比如,子组件中要展示的页面需要发送请求给服务器,使用后端返回的数据进行页面的展示。

    那么,在数据无异常的时候,正常地展示正常的页面;

    在数据出错的时候,告诉用户:网络繁忙请稍后重试!

    在这种情况下我们就可以使用错误边界来解决。

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

    特点

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

    错误边界需要将错误限制在发生错误的组件当中,不要让错误扩散到上层组件。

    使用方式

    getDerivedStateFromError配合componentDidCatch

    state = {
      hasError: false //用于标识子组件是否产生错误
    }
    // 如果当前组件的子组件出现了任何报错,都会调用这个钩子
    static getDerivedStateFromError(error) {
      console.log(error)
      return { hasError: true }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这样的话,渲染子组件时就可以这样:

    {this.state.hasError ? <h2>当前网络不稳定,稍后重试...</h2> : <Child />}
    
    • 1

    如果子组件出现任何报错,都不会干扰到其他组件的渲染,而只是在子组件的位置显示一行字:当前网络不稳定,稍后重试…

    在这里插入图片描述
    上面的代码配合componentDidCatch

    export default class Parent extends Component {
      state = {
        hasError: '' //用于标识子组件是否产生错误
      }
      // 如果当前组件的子组件出现了任何报错,都会调用这个钩子
      static getDerivedStateFromError(error) {
        console.log(error)
        return { hasError: error }
      }
      // 如果组件在渲染的过程中,子组件出现了问题引发了错误,就会调用这个钩子
      componentDidCatch(error, info) {
        console.log(error, info)
        // 这里一般用来统计错误次数,反馈给服务器,用于告知服务器维护人员这里有bug。
      }
    
      render() {
        return (
          <div className='parent'>
            <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
    • 21
    • 22
    • 23
    • 24

    3.render props

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

    Vue中:

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

    React中:

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

    children props

    <A>
    	<B>xxxx</B>
    </A>
    {this.props.children}
    问题:如果B组件需要A组件内的数据 ==> 做不到
    
    • 1
    • 2
    • 3
    • 4
    • 5

    render props

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

    示例

    比如,有一个Parent组件为父组件,A组件为子组件,B组件为孙组件。

    在这里插入图片描述

    A组件自身有一个状态为name,值为tom。B组件如果要想使用A组件的状态的话,正常的做法是:

    Parent组件:
    class Parent extends Component {
      render() {
        return (
        	<div>
          	<A/>
          </div>
        )
      }
    }
    A组件:
    class A extends Component {
      state = { name:'tom' }
      render() {
        return (
        	<div>
          	<B name={this.state.name}/>
          </div>
        )
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    将子组件A的name属性通过props传递给孙组件B。

    不过,我们还可以使用render props来完成:

    Parent组件:
    class Parent extends Component {
      render() {
        return (
        	<div>
          	{/* 2.接收name参数,再传给B */}	
          	<A render={(name) => <B name={name}/>} />
          </div>
        )
      }
    }
    
    A组件:
    class A extends Component {
      state = { name:'tom' }
      render() {
        const { name } = this.state
        return (
        	<div>
          	{/* 1.把name通过this.props.render()传过去 */}
          	{this.props.render(name)}
          </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

    这样,B组件也可以使用到A组件的状态,name被保存在B组件的this.props.name上。

    4.组件通信方式总结

    组件间的关系

    1. 父子组件
    2. 兄弟组件(非嵌套组件)
    3. 祖孙组件(跨级组件)

    几种通信方式

    1. props

      (1)children props

      (2)render props

    2. 消息订阅与发布

      PubSub、event等等

    3. 集中式管理

      redux、dva、mobx等等

    4. Context

      生产者-消费者模式

    比较好的搭配方式

    父子组件:props

    兄弟组件:消息订阅-发布、集中式管理

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

  • 相关阅读:
    Android---StartActivity启动过程
    iOS - 多线程-GCD
    【网页设计】HTML做一个属于我的音乐页面(纯html代码)
    OSG3.6.5帮助文件档编译
    Windows Server 2022 安全功能重大更新
    Java学习-详述main方法、可变参数、数组的工具类、二维数组
    浅谈城市综合管廊分类及其运维管理-Susie 周
    leetcode刷题:栈与队列03(有效的括号)
    Pycharm配置python3环境
    软件定制开发的步骤与注意事项|小程序搭建|APP定制
  • 原文地址:https://blog.csdn.net/Svik_zy/article/details/126126729