关于ReactJS组件通讯

昨天面试的时候被问到一个问题

"非嵌套组件之间的怎么通讯?"

(完全不知道好嘛,而且遇到还有好多的问题都是不知道 T^T

当时的心情:

img-1

img-1

确确实实有被自己菜到,所以不管结局怎么样都要来填一下被挖出来的坑。

父组件向子组件传值

更确切的说应该是组件树中相对上层的组件向其子孙组件传值。

props

都知道数据自上而下流传,使用的就是 props 。

当跨级层次较多、且传的内容较为复杂,建议使用 props.children。举个例子,某子(孙)组件引用了某个组件 Demo,而要向里面穿的参数需要在父(祖)组件确定,这时,可以在父(祖)组件中给 Demo 组件传需要的 props,再把传好了 Props 的这个 Demo 组件作为 props 向下传给子(孙)组件。

虽然这样在父辈组件和子组件之间每一层都需要传一次 Demo 组件,但是如果本身要传的参数很多,只传一个组件的优势就体现出来了。(说实话,内心毫无波动

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class App extends React.Component {
render() {
return (
<div className="App">
<Demo><P color='red' fontSize='16px'/></Demo>
</div>
);
}
}
class Demo extends React.Component {
render(){
return(
<div>
{this.props.children}
</div>
)
}
}
const P = props => <p style={{color: props.color, fontSize: props.fontSize}}>this is demo p</p>

需要注意的是,当且仅当有多个子节点时,props.children 是一个数组。
还需要注意的是,把组件作为普通的 props 来传,其实也是没有什么问题的… 写法与上面的例子不同而已。
普通的 props 上面的例子中也用到了(就是 App 组件调用 P 组件时传了两个 props。

当然啦 props 更适合父组件和子组件的层级差距较小的情况,如果距离较远(跨级较多),还是使用 context 或者 redux

子组件向父组件传值

通常的方式就是在父组件定义一个函数并作为 props 传给子组件,子组件触发该函数以此向父组件传递信息。

举个例子:

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
import React from 'react';
function Demo(props) {
const getNum = e => {
props.fun(e.target.value)
}
return (
<div>
<input onChange={getNum} />
</div>
)
}
export default class App extends React.Component {
state = {
num: 0
}
demoFun = props => {
this.setState({
num: props
})
}
render() {
return (
<div className="App">
<span>{this.state.num}</span>
<Demo fun={this.demoFun} />
</div>
);
}
}

当在子组件 Demo 中监听到 input 输入变动时,就会调用父组件传下来的方法更新父组件的 state 。

非嵌套组件通讯

两个组件之间没有什么联系,比如兄弟组件、不再同一个父组件下的非兄弟组件等。

面试完就在想这个问题,当时的想法是,就算没有什么联系,但是总会有共同的父组件嘛,再状态提升一下,用一下 context 。。。

也不是不可以hhh(不推荐

参考了网路上的博客,推荐使用 events 包,通过设定自定义事件来达到通信的目的(和子组件向父组件传参的思路很像,子组件向父组件传值也可以采用此方法

举个例子:

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
33
34
35
36
37
38
39
40
41
42
43
44
import React from 'react';
import emitter from './ev'
class Foo extends React.Component {
state = {
msg: null,
}
componentDidMount() {
//自定义监听事件
this.eventEmitter = emitter.addListener('demoCallback', msg => {
this.setState({
msg
})
})
}
componentWillUnmount() {
//当组件被消除的时候需要删除监听事件
// emitter.removeAllListeners()
emitter.removeListener(this.eventEmitter)
}
render() {
return (
<div>Foo: {this.state.msg}</div>
)
}
}
class Bar extends React.Component {
getMsg = e => {
// 调用 emitter 的 eimt方法,第一参数为自定义事件名,第二参数为自定义事件的回调函数的参数
emitter.emit('demoCallback', e.target.value)
}
render() {
return (
<div><input type='text' onChange={this.getMsg} /></div>
)
}
}
export default function App() {
return (
<div className="App">
<Foo />
<Bar />
</div>
);
}

上面的例子中 Foo 组件和 Bar 组件是兄弟组件,Foo 组件中定义了一个名为 demoCallback 的监听事件,而在 Bar 组件中调用,依次达到向 Foo 组件传参的目的。

0%