> 文档中心 > 【React扩展】2、PureComponent、ErrorBoundary、render props和组件通信方式总结

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

在这里插入图片描述

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

😇本文目录😇

  • 1.组件优化PureComponent
    • Component的2个问题
    • 效率高的做法
    • 原因
    • 解决
    • 示例1:重写shouldComponentUpdate方法
    • 示例2:PureComponent
  • 2.错误边界ErrorBoundary
    • 理解
    • 特点
    • 使用方式
  • 3.render props
    • 如何向组件内部动态传入带内容的结构(标签)?
    • children props
    • render props
    • 示例
  • 4.组件通信方式总结
    • 组件间的关系
    • 几种通信方式
    • 比较好的搭配方式

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

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

1.组件优化PureComponent

Component的2个问题

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

    this.setState({});// 不改变状态,此时也会重新render()
  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({});但是没有修改数据,组件就不会rendershouldComponentUpdate(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}

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

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

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

示例2:PureComponent

import React, { PureComponent } from 'react'export default class Parent extends PureComponent{}class Child extends PureComponent{}

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

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

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

const obj = this.stateobj.carName = 'Porsche-911 GT3 RS'this.setState(obj)

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

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

正确的更新状态应该是:

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

这样修改数据,就是用一个新对象{ 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 }}

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

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

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

在这里插入图片描述
上面的代码配合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>    )  }}

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组件内的数据 ==> 做不到

render props

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

示例

比如,有一个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>    )  }}

将子组件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>    )  }}

这样,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(开发用的少,封装插件用的多)