一起养成写作习惯!这是我参与「掘金日新计划 · 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组件传递过来的数据。
我们来总结下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增加了。