React 组件间的通信示例
前言
从官网上也有介绍组件间如何通信,但不够详细,这里做个小结,方便对比和回顾
本文内容
处理组件之间的通信,主要取决于组件之间的关系,因此我们划分为以下三种:
- 【父组件】向【子组件】传值;
- 【子组件】向【父组件】传值;
- 【组件A】向无关系【组件B】传值,一般为兄弟组件;
一、「父组件」向「子组件」传值
这是最普遍的用法,实现上也非常简单,主要是利用props来实现
//父组件 importReactfrom'react'; importSonfrom'./components/son'; classFatherextendsReact.Component{ constructor(props){ //这里要加super,否则会报错 super(props); this.state={ checked:true } } render(){ return() } }
//子组件 classSonextendsReact.Component{ render(){ //接收来自父组件的参数 letchecked=this.props.checked, text=this.props.text; return( ) } }
多想一点:
如果组件的嵌套层次太多,那么从外到内的交流成本就会加深,通过props传值的优势就不明显,因此,我们还是要尽可能的编写结构清晰简单的组件关系,既也要遵循组件独立原则,又要适当控制页面,不可能或极少可能会被单用的代码片,可不编写成一个子组件
二、「子组件」向「父组件」传值
我们知道,react的数据控制分为两种,为props和state;其中,props如上刚介绍过,它是父组件向子组件传值时作为保存参数的数据对象;而state是组件存放自身数据的数据对象。这两者最主要的区别就是,props属于父组件传给子组件的只读数据,在子组件中不能被修改,而state在自身组件中使用时,可以通过setState来修改更新。
子组件向父组件传值,需要控制自己的state,并发起父组件的事件回调来通知父组件
//父组件 importSonfrom'./components/son'; classFatherextendsReact.Component{ constructor(props){ super(props) this.state={ checked:false } } onChildChanged(){ this.setState({ checked:newState }) } render(){ letisChecked=this.state.checked?'yes':'no'; return(Areyouchecked:{isChecked}) } }
//子组件 classSonextendsReact.Component{ constructor(props){ super(props); this.state={ checked:this.props.initialChecked } } onTextChange(){ letnewState=!this.state.check.checked; this.setState({ checked:newState }); //注意,setState是一个异步方法,state值不会立即改变,回调时要传缓存的当前值, //也可以利用传递一个函数(以上传的是对象),并传递prevState参数来实现数据的同步更新 this.props.callbackParent(newState); } render(){ lettext=this.props.text; letchecked=this.state.checked; return( ) } }
多想一点:
- 同样应当避免深层次的组件嵌套
- 这里其实是依赖了props来传递事件的引用,并通过回调的方式来实现,在没有使用工具情况下,可以使用该办法
拓展一点:
在onChange事件或者其他React事件中,你能获取以下信息:
- 「this」指向你的组件
- 「一个参数」一个react合成事件,SyntheticEvent
我们知道,React对所有事件的管理都是自己封装实现的,html中的onclick被封装成了onClick,onchange被封装成了onChange。从根本上来说,他们都是被绑定在body上的。
多个子组件回调同一个回调函数情况
父组件中大概率包含多个子组件,为节省和简洁代码,遵循don'trepeatyourself原则,我们会让一个回调函数实现多个子组件的功能,或多个组件协作完成指定功能
importReactfrom'react'; importSonfrom'./components/son'; classFatherextendsReact.Componnet{ constructor(props){ super(props); this.state={ totalChecked:0 } } onChildChangeed(){ letnewTotal=this.state.totalChecked+(newState?1:-1); this.setState({ totalChecked=this.state.totalChecked; }); } render(){ return() } }Checkednumbers:{this.state.totalChecked}
//子组件 classSonextendsReact.Component{ constructor(props){ super(props); this.state={ checked:this.props.initialChecked } } onTextChange(){ letnewState=!this.state.checked; this.setState({ checked:newState }) //setState异步方法问题,注意传值 this.props.callbackParent(newState); } render(){ lettext=this.props.checked; letchecked=this.state.checked; return{ } } }
多想一点:
在本案例中,我们引用了三个Son子组件,每个Son组件都独立工作互不干扰,该例中,增加了一个totalChecked来替代之前的checked,当组件触发onTextChange后,触发父组件的回调函数使得父组件的值得以改变。
三、组件A和无关系组件B之间的通信
如果组件之间没有任何关系,或者组件嵌套的层次比较深,或者,你为了一些组件能够订阅,写入一些信号,不想让两个组件之间插入一个组件,而是让两个组件出于独立的关系。对于时间系统,有两个基本操作:
- 订阅:subscribe
- 监听:listen
并发送send/触发trigger/发布publish/发送dispatch通知那些想要的组件
1.EventEmitter/Target/Dispatcher
特点:需要一个指定的订阅源
//tosubscribe otherObiect.addEventListener('clickEvent',function(){ alert('click!'); }) //todispatch this.dispatchEvent('clickEvent');
2.Publish/Subscribe
特点:触发的时候,不需要指定一个特定的源,使用全局对象广播的方式来处理事件
//tosubscribe globalBroadcaster.subcribe('clickEvent',function(){ alert('cilck!'); }) //topublish globalBroadcaster.publish('clickEvent');
这种方案还有一个插件可用,即PubSubJs;用法如下:
importPubsubfrom'pubsub-js'; ... //tosubscribe Pubsub.subscribe('EVENT',(msg,param)=>{ console.log(msg,param); }); //topublish Pubsub.publish('EVENT',param);
3.Single
特点:与EventEmitter/Target/Dispatcher类似,但是不要使用随机字符串作为事件触发的引用。触发事件的每一个对象都需要一个确切的名字,并且在触发的时候,也必须要指定确切的事件
//tosubscribe otherObject.clicked.add(function(){ alert('click'); }) //todispatch this.clicked.dispatch();
React团队使用的是:js-signals它基于Signals模式,用起来相当不错。
事件订阅与取消
使用React事件的时候,必须关注以下两个方法:
- componentDidMount
- componentWillUnmount
在componentDidMount事件中,等待组件挂载mounted完成,再订阅事件;订阅的事件需要在组件卸载componentWillUnmount的时候取消事件的订阅。
因为组件的渲染和销毁是有React来控制的,我们不知道怎么引用他们,所以EventEmitter模式在处理事件的时候用处不大,Pub/Sub模式就好用些,因为我们不需要知道引用在哪。
ES6策略:yieldandjs-csp
ES6中有一种传递信息的方式,使用生成函数generators和yield关键字,用法参考以下例子
importcspfrom'js-csp'; function*list(){ for(vari=0;i结束语
数据在组件中应该以什么样的方式进行传递取决于组件之间存在什么样的关系和当时的业务场景需求,大家应该根据项目合理选择数据处理的方案,这很可能减少你大量的代码量和代码逻辑。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。