最近写了一点 React 的应用,遇到了 React 各个组件之间的通信问题(1.父组件向子组件传值,2.子组件向父组件传值,3.平级组件间相互传值),特此整理一下。
React 之间的层级关系
假设我们开发的是一款纯 React 应用,那么有可能有如下的层级结构关系
- 父组件向子组件之间通信
- 子组件向父组件之间通信
- 没有嵌套关系的组件间通信
下面我们探讨一下各种层级关系之间的通信方式
父组件向子组件之间通信
这个功能实现起来相当容易,主要是通过 props 来传值,这在 React 的开发中是最普遍使用的一种方式。
例子:
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
| import React from 'react'; import ReactDOM from 'react-dom';
class Father extends React.Component { constructor(props) { super(props); this.state = {value: null}; this.textChange = this.textChange.bind(this); }
textChange(e) { this.setState({value: e.currentTarget.value}); }
render() { return ( <div> 请输入数字 (父元素):<input type="tel" onChange={this.textChange}/> <Child text={this.state.value}/> </div> ); } }
class Child extends React.Component { render() { return ( <div> 输入的数字 (子元素):<span>{this.props.text}</span> </div> ); } }
ReactDOM.render(( <Father /> ), document.getElementById('ValuePropagation'));
|
效果:
PS: 若是嵌套层级较深,从外到内通信的成本就会愈发增加,所以书写的时候尽量减少嵌套的层级
子组件向父组件之间通信
子组件向父组件传值,我们可以依赖 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| import React from 'react'; import ReactDOM from 'react-dom';
class Father extends React.Component { constructor(props) { super(props); this.state = {value: null}; this.textChange = this.textChange.bind(this); }
textChange(newValue){ this.setState({value:newValue}); }
render() { return ( <div> 输入的数字 (父元素):<span>{this.state.value}</span> <Child callBack={this.textChange}/> </div> ); } }
class Child extends React.Component {
constructor(props) { super(props); this.textChange = this.textChange.bind(this); }
textChange(e){ let value = e.currentTarget.value; this.props.callBack(value); }
render() { return ( <div> 请输入数字 (子元素):<input type="tel" onChange={this.textChange}/> </div> ); } }
ReactDOM.render(( <Father /> ), document.getElementById('ValuePropagation'));
|
效果:
没有嵌套关系的组件间通信
没有嵌套关系的组件之间的通信可以使用 Publish / Subscribe 模式,这边自用的是 PubSubJS,采用的是全局广播的方式。
例子:
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
| import React from 'react'; import ReactDOM from 'react-dom'; import PubSub from 'pubsub-js';
class Element_One extends React.Component { constructor(props) { super(props); this.state = {value: null}; }
componentDidMount(){ this.pubsub = PubSub.subscribe('textChange',(topic,newValue)=>{ this.setState({value: newValue}); }); }
componentWillUnmount(){ PubSub.unsubscribe(this.pubsub); }
render() { return ( <div> 输入的数字 (Element_one):<span>{this.state.value}</span> </div> ); } }
class Element_Two extends React.Component {
constructor(props) { super(props); this.textChange = this.textChange.bind(this); }
textChange(e){ let value = e.currentTarget.value; PubSub.publishSync('textChange',value); }
render() { return ( <div> 请输入数字 (Element_Two):<input type="tel" onChange={this.textChange}/> </div> ); } }
ReactDOM.render(( <div> <Element_One /> <Element_Two /> </div> ), document.getElementById('ValuePropagation'));
|
效果:
以上的例子在小型的应用场景里面还是比较适用的,但是遇到大型的项目,使用回调函数,或是全局广播的方式就显得有欠妥当,因为没有一个独立的地方去管理整个业务的事件逻辑,会使得整个代码显得凌乱,也为之后的维护工作带来隐忧。所以按照实际需求使用是最恰当的。有兴趣的朋友们也可以使用 Javascript 状态管理容器 Redux,相信是个不错的选择。