1.1.5 react 父子组件通信
React 数据流动是单向的; 父组件向子组件通信: props
子组件向父组件通信: 回调函数/自定义事件
跨级组件通信: 层层组件传递props/context
没有嵌套关系组件之间的通信: 自定义事件
父组件向子组件通信
通过 props
# 1.父组件调用子组件传递参数
# parent.js
class Parent extends Component {
render() {
return (
<div>
<Child name="testparam" />
</div>
);
}
}
# child.js
import React from 'react';
import PropTypes from 'prop-types';
class Child extends Component {
render() {
return (
<div>{this.props.name}</div>
);
}
}
Child.propTypes = {
name: PropTypes.string.isRequired
};
export default Child;
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
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
2.子组件向父组件通信
利用回调函数
利用自定义事件机制
父组件中:
hideConponent() {
this.setState({
isShowChild: false,
});
}
render() {
return (
<div>
<child hideConponent={this.hideConponent.bind(this)} />
</div>
);
}
子组件中:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class Child extends Component {
static propTypes = {
hideConponent: PropTypes.func.isRequired,
}
render() {
return (
<div>
this is child
<button onClick={this.props.hideConponent}>点击 child </button>
</div>
);
}
}
export default Child;
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
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
- 子组件传值到父组件
父组件中:
hideConponent(val) {
console.log(val);
this.setState({
isShowChild: false,
});
}
render() {
return (
<div>
<child hideConponent={this.hideConponent.bind(this)} />
</div>
);
}
子组件中:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class Child extends Component {
static propTypes = {
hideConponent: PropTypes.func.isRequired,
}
handleConponent() {
let val = '123';
this.props.hideConponent(val);
}
render() {
return (
<div>
this is child
<button onClick={this.handleConponent.bind(this)}>点击 child </button>
</div>
);
}
}
export default Child;
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
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
3.跨级组件通信
当需要让子组件跨级访问信息时,我们可以像之前说的方法那样向更高级别的组件层层传递 props,
但此时的代码显得不那么优雅,甚至有些冗余。
在 React 中,我们还可以使用 context 来 实现跨级父子组件间的通信;
不过 React 官方并不建议大量使用 context,因为尽管它可以减少逐层传递,
但当组件结 构复杂的时候,我们并不知道 context 是从哪里传过来的。
Context 就像一个全局变量一样,而 全局变量正是导致应用走向混乱的罪魁祸首之一,
给组件带来了外部依赖的副作用。
# 父组件
// 提供一个函数,用来返回相应的context对象
getChildContext() {
return {
color: 'red'
};
}
render() {}
HomeIndex.childContextTypes = {
color: PropTypes.string
};
childContextTypes必须得设置,不设置在各下级组件中无法获取到数据,这项是必须项;
不设置会报这样的错误:Index.getChildContext(): childContextTypes must be defined in order to use getChildContext().
# 子组件
Child.contextTypes = {
color: PropTypes.string
};
render() {
return (
<div>
<button>child context {this.context.color} </button>
</div>
);
}
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
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
子组件在《深入 React 技术栈》 中看到使用
static contextTypes = {
color: PropTypes.string
};
1
2
3
2
3
可是我这里报错,
如果 babel设置为 es6 的转码方式,会报错,因为定义静态属性不属于 es6,而在 es7 的草案中。
ES6 的 class 中只有静态方法,没有静态属性。
//由于是用ES6 class语法创建组件,其内部只允许定义方法,而不能定义属性,
class的属性只能定义在 class 之外。 所以 contextTypes 要写在组件外部。
Child.contextTypes = {
color: PropTypes.string
};
1
2
3
2
3
4.没有嵌套关系的组件通信
使用自定义事件机制
没有嵌套关系的,那只能通过可以影响全局的一些机制去考虑。刚才讲到的自定义事件机制 不失为一种上佳的方法。
我们就以常用的发布/订阅模式来举例,这里借用 Node.js Events 模块的浏览器版实现。
import { EventEmitter } from 'events';
export default new EventEmitter();
# 然后把 EventEmitter 实例输出到各组件中使用:
import ReactDOM from 'react-dom';
import React, { Component, PropTypes } from 'react';
import emitter from './events';
class ListItem extends Component {
static defaultProps = { checked: false, }
constructor(props) { super(props); }
render() {
return (
<li>
<input type="checkbox" checked={this.props.checked} onChange={this.props.onChange} />
<span>{this.props.value}</span>
</li>
);
}
}
class List extends Component {
constructor(props) {
super(props);
this.state = {
list: this.props.list.map(entry => ({
text: entry.text,
checked: entry.checked || false,
})),
};
}
onItemChange(entry) {
const { list } = this.state;
this.setState({
list: list.map(prevEntry => ({
text: prevEntry.text,
checked: prevEntry.text === entry.text ? !prevEntry.checked : prevEntry.checked,
}))
});
emitter.emit('ItemChange', entry);
}
render() {
return (
<div>
<ul>
{this.state.list.map((entry, index) => (
<ListItem
key={`list-${index}`}
value={entry.text}
checked={entry.checked}
onChange={this.onItemChange.bind(this, entry)} />
))}
</ul>
</div>);
}
}
class App extends Component {
componentDidMount() {
this.itemChange = emitter.on('ItemChange', (data) => {
console.log(data);
});
}
componentWillUnmount() {
emitter.removeListener(this.itemChange);
}
render() {
return (<List list={[{text: 1}, {text: 2}]} />);
}
}
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68