子组件A通过回调函数的形式将数据传递给父组件,父组件再通过属性将数据传递给子组件B
代码示例如下:
父组件
class CommentApp extends Component {
constructor() {
super()
this.state = {
info: []
}
}
handleSubmit(con) {
this.state.info.push(con);
this.setState({info: this.state.info})
}
render() {
return (
<div>
{/* 兄弟组件之间的传值 */}
<CommentInput onSubmit={this.handlecom.bind(this)}/>
<CommentList comList={this.state.info}/>
</div>
)
}
}
子组件A
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class CommentInput extends Component{
handleclick=()=>{
let val1=this.refs.inp.value;
let val2=this.refs.area.value;
if(this.props.onSubmit){
this.props.onSubmit({val1,val2});
}
}
render(){
return(
<div>
<input ref="inp" style={{
display:'block',
width:300,
marginTop:20,
marginLeft:30
}}/>
<textarea ref="area" style={{
display:'block',
width:300,
height:200,
marginTop:20,
marginLeft:30
}}/>
<button
onClick={this.handleclick.bind(this)}
style={{
marginTop:20,
marginLeft:250,
marginBottom:30
}}>
发布评论</button>
</div>
)
}
}
export default CommentInput;
子组件B
import React, { Component } from 'react';
class CommentList extends Component{
render(){
return(
<ul>
{
this.props.comList.map((item,input)=>{
return(
<li key={input}>
<span>{item.val1}</span>---
<span>{item.val2}</span>
</li>
)
})
}
</ul>
)
}
}
export default CommentList;
在子组件A中 commentDidMount函数中,发布事件,在子组件B中commentDidMount函数中对事件进行监听
什么是事件的发布订阅模式?
假如我家有一个水果店,张三来我家买苹果,正好我家苹果卖完了,我就告诉他一个电话号码,让他回去等电话,苹果一到货,我就通知他(但是他可以选择再去买或者不去买,只是我一定会通知他),同样李四也是同样的需求,我给出了同样的解决方案,以此类推。。。等苹果到货了,我会按照先后顺序挨个给所有的预订者打电话通知。在这个事件当中,张三和李四就是订阅者,而我就是发布者,我打电话通知他们的动作就是发布者对订阅事件的处理函数。所以在react组件之间的通信场景里,只要组件1负责订阅(监听事件),组件2负责发布(触发事件)就可以进行数据交流。
接下来通过一个小demo来演示发布订阅模式
首先需要写一个事件代理类作为发布者和订阅者的中介,这个事件代理类的实现模仿的是node.js底层的events包,代码如下:
const events={
onObj: {},
// 订阅事件
// 把订阅事件存到对应类型的数组序列中
addListener:function(key, fn) {
if(this.onObj[key] === undefined) {
this.onObj[key] = [];
}
const args = [].concat(Array.prototype.slice.call(arguments, 1));
for(let i = 0; i < args.length ; i++) {
this.onObj[key].push(args[i]);
}
},
// 取消订阅事件
removeListener:function(key) {
this.onObj[key] = [];
},
// 发布事件
// 在事件列表中找到对应的事件类型,事件类型对应的内容不为空,则可以遍历数组,触发事件
emit:function() {
let key, args;
if(arguments.length == 0) {
return false;
}
key = arguments[0];
args = [].concat(Array.prototype.slice.call(arguments, 1));
if(this.onObj[key] !== undefined
&& this.onObj[key].length > 0) {
for(let i in this.onObj[key]) {
this.onObj[key][i].call(null, args[i]);
}
}
}
}
export default events;
订阅者
import React, { Component } from 'react';
import emitter from './events'
class Subscription extends Component {
constructor(props) {
super(props);
this.state={
msg:''
}
}
// 在组件挂载完后声明一个自定义事件
componentDidMount(){
emitter.addListener('callMe',(msg)=>{
this.setState({
msg:msg
})
})
}
// 在组件卸载的前移除事件监听
componentWillMount(){
emitter.removeListener('callMe',(msg)=>{
this.setState({
msg:msg
})
})
}
render() {
return (
<div>我是订阅者,订阅到的消息为:{this.state.msg}</div>
);
}
}
export default Subscription;
发布者
import React, { Component } from 'react';
import emitter from './events'
class Announce extends Component{
render(){
function publ(){
return function(){
emitter.emit('callMe','尊敬的客户,苹果到货啦');
}
}
return(
<div>
我是负责发布事件的组件<button onClick={publ()}>点击我</button>
</div>
);
}
}
export default Announce;
react推崇的是单向数据流,自上而下进行数据的传递,但是由下而上或者不在一条数据流上的组件之间的通信就会变的复杂。解决通信问题的方法很多,如果只是父子级关系,父级可以将一个回调函数当作属性传递给子级,子级可以直接调用函数从而和父级通信。
组件层级嵌套到比较深,可以使用上下文getChildContext来传递信息,这样在不需要将函数一层层往下传,任何一层的子级都可以通过this.context直接访问。
context相当于提供了一个全局可以访问的对象,组件之间没有嵌套关系也可以使用;
只有对那些每个组件都可能使用,但中间组件又可能不使用的对象才有必要使用context,要尽可能地减少使用context
对于父组件,需要通过一个静态属性childContextTypes声明提供给子组件的Context对象的属性,并实现一个实例getChildContext方法,返回一个代表Context的纯对象 (plain object) 。
子孙组件需要在组件中申明contextTypes,指定要接收的context结构类型,否则context将是一个空对象
子孙组件通过this.context来获取上下文对象
下面通过一个demo来展示context的数据传递
import React, { Component } from 'react';
import PropTypes from 'prop-types'
import './App.css';
class MiddleComponent extends Component {
// 声明需要使用的Context属性
static contextTypes = {
propA: PropTypes.string
}
render() {
return (
<div className="son" style={{border:'1px solid blue',width:'60%',margin:'50px auto',textAlign:'center'}}>
<p>子组件,获取父组件的值:{this.context.propA}</p>
<ChildComponent/>
</div>
);
}
}
class ChildComponent extends Component {
// 声明需要使用的Context属性
static contextTypes = {
propA: PropTypes.string
}
render() {
return (
<div className="son" style={{border:'1px solid green',width:'60%',margin:'50px auto',textAlign:'center'}}>
<p>孙组件,获取传递下来的值:{this.context.propA}</p>
</div>
);
}
}
class App extends Component {
// 声明Context对象属性
static childContextTypes = {
propA: PropTypes.string
}
// 返回Context对象,方法名是约定好的
getChildContext() {
return {
propA: 'hello'
}
}
render() {
return (
<div className="App" style={{ border: '1px solid red', width: '30%', margin: '50px auto', textAlign: 'center' }}>
<p style={{ padding: '0', margin: '0' }}>父组件</p>
<MiddleComponent />
</div>
)
}
}
export default App;