EventEmitter

EventEmitter

非嵌套关系的组件实现通信

componentDidMount事件中,如果组件挂载完成,再订阅事件;在组件卸载的时候,在componentWillUnmount事件中取消事件的订阅; 以常用的发布/订阅模式举例,借用Node.js Events模块的浏览器版实现

实现这样一个功能: 点击List2中的一个按钮,改变List1中的信息显示

1
npm install events --save

src下新建一个util目录里面建一个events.js

1
2
3
import { EventEmitter } from 'events';

export default new EventEmitter();
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
26
27
28
29
30
31
32
// list1.jsx
import React, { Component } from 'react';
import emitter from '../util/events';

class List extends Component {
constructor(props) {
super(props);
this.state = {
message: 'List1',
};
}
componentDidMount() {
// 组件装载完成以后声明一个自定义事件
this.eventEmitter = emitter.addListener('changeMessage', (message) => {
this.setState({
message,
});
});
}
componentWillUnmount() {
emitter.removeListener(this.eventEmitter);
}
render() {
return (
<div>
{this.state.message}
</div>
);
}
}

export default List;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// List2.jsx
import React, { Component } from 'react';
import emitter from '../util/events';

class List2 extends Component {
handleClick = (message) => {
emitter.emit('changeMessage', message);
};
render() {
return (
<div>
<button onClick={this.handleClick.bind(this, 'List2')}>点击我改变List1组件中显示信息</button>
</div>
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// APP.jsx
import React, { Component } from 'react';
import List1 from './components/List1';
import List2 from './components/List2';


export default class App extends Component {
render() {
return (
<div>
<List1 />
<List2 />
</div>
);
}
}

自定义事件是典型的发布订阅模式,通过向事件对象上添加监听器和触发事件来实现组件之间的通信

(2)辅助 toolbox 实现 createdestory

需求是一个长按弹出的 toolbox 框框,要实现点击其它非 toolbox 区域框框消失的功能

之前的方式:

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
// utils/toolbox.js
class ToolboxCtrl {

create(args) {
//...
render(
<this.tb
{...props}
handleClickAway={() => this.destory()}
/>,
div
)
//...
}

destory() {}

}

// com/toolbox.js
const ToolBox = ({ handleClickAway }) => {
const ref = useClickAway(() => handleClickAway())
const handleItemClick = e => handleClickAway()
return (<div ref={ref} onClick={e => handleItemClick(e)}>...</div>)
}

useClickAway 是自定义的 Hooks

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
26
27
28
29
30
31
export default function useClickAway(
onClickAway,
dom,
eventName = 'click'
) {

const element = useRef()

const handler = useCallback(
event => {
const targetElement = typeof dom === 'function' ? dom() : dom
const el = targetElement || element.current
if (!el || el.contains(event.target)) {
return;
}

onClickAway(event)
},
[element.current, onClickAway, dom],
)

useEffect(() => {
document.addEventListener(eventName, handler)

return () => {
document.removeEventListener(eventName, handler)
}
}, [eventName, handler])

return element
}

一般情况下没有问题…但是中间遇到很奇怪的bug…原因没有研究出来

最后还是用 eventEmitter 解决的

1
2
3
4
5
6
7
8
9
10
11
12
13
// com/toolbox.js
const ToolBox = ({ handleClickAway }) => {
useEffect(() => {
emitter.on('toolbox.destory', handleClickAway)
return () => emitter.off('toolbox.destory', handleClickAway)
}, [])
const ref = useClickAway(() => handleClickAway())
const handleItemClick = e => handleClickAway()
return (<div ref={ref} onClick={e => handleItemClick(e)}>...</div>)
}

// 在点击某个元素的时候, 以实现 useClickAway 的效果
emitter.emit('toolbox.destory')
-------------要说再见啦感谢大佬的光临~-------------