react基础(十一)

195 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情

前言

大家好呀,我是L同学。在上篇文章中react基础(十),我们学习了react性能优化方面的知识。接下来,我们继续学习react中的事件总线和ref的使用和类型。

事件总线

在前面的文章中,我们学习过context,它可以实现数据的共享。但是在开发中如果有跨组件的事件传递,那该怎么办呢?我们可以通过events事件总线来实现跨组件的事件传递。

首先我们需要yarn来安装events。

yarn add events

我们通过案例演练下。App组件中有Home组件和Profile组件,这两个组件是兄弟关系,Profile组件点击按钮将数据传递给Home组件。

import React, {PureComponent} from 'react'
import {EventEmitter} from 'events'

export default class App extends PureComponent {
  render() {
    return (
      <div>
        <Home/>
        <Profile/>
      </div>
    )
  }
}

导入完events库之后,需要创建eventBus对象。

const eventBus = new EventEmitter()

在Profile组件中通过eventBus.emit('事件名称', 参数列表)发出事件

class Profile extends PureComponent {
  render() {
    return (
      <div>
        profile
        <button onClick={e => this.emitEvent()}>点击profile按钮</button>
      </div>
    )
  }

  emitEvent() {
    eventBus.emit('sayHello', 'hello home', 123)
  }
}

在Home组件中的componentDidMount生命周期方法中通过eventBus.addListener('事件名称', 监听函数)去监听事件。在componentWillUnmount生命周期方法中通过eventBus.removeListener('事件名称', 监听函数)移除事件。

class Home extends PureComponent {
 componentDidMount() {
   eventBus.addListener('sayHello', this.handleSayHelloListener)
 }

 componentWillUnmount() {
   eventBus.removeListener('sayHello', this.handleSayHelloListener)
 }

 handleSayHelloListener(message, num) {
   console.log(message, num);
 }

 render() {
   return <div>Home</div>
 }
}

我们可以看到点击Profile组件中的按钮,在Home组件中可以获取到Profile组件传递过来的数据。

image.png 我们来总结下events常用的API。

  • 创建EventEmitter对象:eventBus对象
  • 发出事件:eventBus.emit('事件名称', 参数列表)
  • 监听事件:eventBus.addListener('事件名称', 监听函数)
  • 移除事件:eventBus.removeListener('事件名称', 监听函数)

ref的使用

在有些情况下,我们需要获取到DOM进行某些操作。我们可以通过创建refs来获取对应的DOM。

方式一 传入字符串(不推荐)

使用时通过this.refs.传入的字符串格式来获取对应的元素。

import React, {PureComponent} from 'react'

export default class App extends PureComponent {
  render() {
    return (
      <div>
        <h2 ref='titleRef'>hello react</h2>
        <button onClick={e => this.changeText()}>改变文本</button>
      </div>
    )
  }

  changeText() {
    this.refs.titleRef.innerHTML = 'hello haha'
  }
}

方式二 传入一个对象(推荐)

通过React.reactRef()方式创建一个对象,使用时获取到创建的对象其中有一个current属性就是对应的元素。

首先我们需要引入createRef。

import React, {PureComponent, createRef} from 'react'

然后在constructor方法中通过createRef()方法创建出一个对象。

  constructor(props) {
    super(props) 
    this.titleRef = createRef()
  }

然后在元素中的ref属性中传入这个对象。

<h2 ref={this.titleRef}>hello react</h2>

使用时获取到创建的对象其中有一个current属性就是对应的元素。

  changeText() {
    this.titleRef.current.innerHTML = 'hello xixi'
  }

方式三 传入一个函数

该函数会在DOM被挂载时进行回调,这个函数会传入一个元素对象,我们可以自己保存。使用时,直接拿到之前保存的元素对象就可以了。

  constructor(props) {
    this.titleEl = null
  }

元素中的ref属性中传入一个回调函数。

<h2 ref={arg => this.titleEl = arg}>hello react</h2>
  changeText() {
    this.titleEl.innerHTML = 'hello lala'
  }

ref的类型

ref除了用于html元素,还可以用于组件。当ref属性用于html元素时,构造函数中使用React.createRef()创建的ref接收底层dom元素作为它的current属性。当ref属性应用于自定义class组件时,ref对象接收组件的挂载实例作为它的current属性。

需要注意的是,不能在函数组件上使用ref属性,因为它们没有实例。

首先,我们创建Counter组件。

class Counter extends PureComponent {
  constructor(props) {
    super(props)
    this.state = {
      counter: 0
    }
  }

  render() {
    return (
      <div>
        <h2>当前计数:{this.state.counter}</h2>
        <button onClick={e => this.increment()}>+1</button>
      </div>
    )
  }

  increment() {
    this.setState({
      counter: this.state.counter + 1
    })
  }
}

然后在App组件中嵌套Counter组件,通过ref获取到Counter组件,调用它的increment方法。

export default class App extends PureComponent {
  constructor(props) {
    super(props)
    this.counterRef = createRef()
  }
  render() {
    return (
      <div>
        <Counter ref={this.counterRef} />
        <button onClick={e => this.appBtnClick()}>App按钮</button>
      </div>
    )
  }

  appBtnClick() {
    this.counterRef.current.increment()
  }
}

我们可以看到点击App组件的App按钮,Counter组件的数据counter增加了。

image.png