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

发布时间:2024-08-27 12:01

前言

博主主页👉🏻蜡笔雏田学代码
专栏链接👉🏻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>
    )
  }
}

效果
\"在这里插入图片描述\"

此时在点击完按钮之后,当前组件的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>
    )
  }
}

效果
\"在这里插入图片描述\"

此时子组件接收到的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{}
...

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

//错误写法
const obj = this.state
obj.carName = '宝马'
this.setState(obj) 

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

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

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

2. render props

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

Vue中:

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

<A>
  <B/>
</A>

React中:

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

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

children props

<A>
  <B>xxxx</B>
</A>
{this.props.children}
问题: 如果B组件需要A组件内的数据, ==> 做不到 
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>
    )
  }
}

效果

\"在这里插入图片描述\"

render props

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

如果在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>
    )
  }
}

效果

\"在这里插入图片描述\"

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>
    )
  }
}
// 如果组件在渲染的整个过程中,由于子组件出现了问题,引发一些错误,
//就会调用此函数,用来统计错误,反馈给服务器,用于通知编码人员进行bug的解决
componentDidCatch(error, info) {
  // 统计页面的错误。发送请求发送到后台去
    console.log(error, info);
}

效果

\"在这里插入图片描述\"

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}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号