前言
從官網(wǎng)上也有介紹組件間如何通信,但不夠詳細(xì),這里做個(gè)小結(jié),方便對(duì)比和回顧
本文內(nèi)容
處理組件之間的通信, 主要取決于組件之間的關(guān)系,因此我們劃分為以下三種:
一、「父組件」向「子組件」傳值
這是最普遍的用法,實(shí)現(xiàn)上也非常簡(jiǎn)單,主要是利用props來實(shí)現(xiàn)
// 父組件 import React from 'react'; import Son from './components/son'; class Father extends React.Component { constructor(props) { // 這里要加super,否則會(huì)報(bào)錯(cuò) super(props); this.state = { checked: true } } render() { return ( <Son text="Toggle me" checked={this.state.checked} /> ) } }
// 子組件 class Son extends React.Component { render() { // 接收來自父組件的參數(shù) let checked = this.props.checked, text = this.props.text; return ( <label>{text}: <input type="checkbox" checked={checked} /></label> ) } }
多想一點(diǎn):
如果組件的嵌套層次太多,那么從外到內(nèi)的交流成本就會(huì)加深,通過 props 傳值的優(yōu)勢(shì)就不明顯,因此,我們還是要盡可能的編寫結(jié)構(gòu)清晰簡(jiǎn)單的組件關(guān)系, 既也要遵循組件獨(dú)立原則,又要適當(dāng)控制頁面,不可能或極少可能會(huì)被單用的代碼片,可不編寫成一個(gè)子組件
二、「子組件」向「父組件」傳值
我們知道,react的數(shù)據(jù)控制分為兩種,為 props 和 state;其中,props 如上剛介紹過,它是父組件向子組件傳值時(shí)作為保存參數(shù)的數(shù)據(jù)對(duì)象;而 state 是組件存放自身數(shù)據(jù)的數(shù)據(jù)對(duì)象。這兩者最主要的區(qū)別就是,props屬于父組件傳給子組件的只讀數(shù)據(jù),在子組件中不能被修改,而state在自身組件中使用時(shí),可以通過setState來修改更新。
子組件向父組件傳值,需要控制自己的state,并發(fā)起父組件的事件回調(diào)來通知父組件
// 父組件 import Son from './components/son'; class Father extends React.Component { constructor(props) { super(props) this.state = { checked: false } } onChildChanged() { this.setState({ checked: newState }) } render() { let isChecked = this.state.checked ? 'yes' : 'no'; return ( <div> <span>Are you checked: {isChecked }</span> <Son text="Toggle me" initialChecked={this.state.checked} callbackParent={this.onChildChanged.bind(this)} ></Son> </div> ) } }
// 子組件 class Son extends React.Component { constructor(props) { super(props); this.state = { checked: this.props.initialChecked } } onTextChange() { let newState = !this.state.check.checked; this.setState({ checked: newState }); // 注意,setState 是一個(gè)異步方法,state值不會(huì)立即改變,回調(diào)時(shí)要傳緩存的當(dāng)前值, // 也可以利用傳遞一個(gè)函數(shù)(以上傳的是對(duì)象),并傳遞prevState參數(shù)來實(shí)現(xiàn)數(shù)據(jù)的同步更新 this.props.callbackParent(newState); } render() { let text= this.props.text; let checked = this.state.checked; return ( <label>{text}: <input type="checkbox" checked={checked} onChange={this.onTextChange.bind(this)}></label> ) } }
多想一點(diǎn):
拓展一點(diǎn):
在onChange 事件或者其他React事件中,你能獲取以下信息:
我們知道,React對(duì)所有事件的管理都是自己封裝實(shí)現(xiàn)的,html中的 onclick 被封裝成了 onClick, onchange 被封裝成了 onChange。從根本上來說,他們都是被綁定在body上的。
多個(gè)子組件回調(diào)同一個(gè)回調(diào)函數(shù)情況
父組件中大概率包含多個(gè)子組件,為節(jié)省和簡(jiǎn)潔代碼,遵循 don't repeat yourself 原則,我們會(huì)讓一個(gè)回調(diào)函數(shù)實(shí)現(xiàn)多個(gè)子組件的功能,或多個(gè)組件協(xié)作完成指定功能
import React from 'react'; import Son from './components/son'; class Father extends React.Componnet { constructor(props) { super(props); this.state = { totalChecked: 0 } } onChildChangeed() { let newTotal = this.state.totalChecked + (new State ? 1 : -1 ); this.setState({ totalChecked = this.state.totalChecked; }); } render() { return ( <div> <div>Checked numbers: {this.state.totalChecked}</div> <Son text="Toggle me" initialChecked={this.state.checked} callbackParent={this.onChildChanged} /> <Son text="Toggle me too" initialChecked={this.state.checked} callbackParent={this.onChildChanged} /> <Son text="Add me" initialChecked={this.state.checked} callbackParent={this.onChildChanged} /> </div> ) } }
// 子組件 class Son extends React.Component { constructor(props) { super(props); this.state = { checked: this.props.initialChecked } } onTextChange() { let newState = !this.state.checked; this.setState({ checked: newState }) // setState異步方法問題,注意傳值 this.props.callbackParent(newState); } render() { let text = this.props.checked; let checked = this.state.checked; return { <label>{text}: <input type="checkbox" checked={checked} onChange={this.onTextChange.bind(this)} /></label> } } }
多想一點(diǎn):
在本案例中,我們引用了三個(gè) Son 子組件, 每個(gè) Son 組件都獨(dú)立工作互不干擾,該例中,增加了一個(gè) totalChecked 來替代之前的 checked, 當(dāng)組件觸發(fā)onTextChange 后,觸發(fā)父組件的回調(diào)函數(shù)使得父組件的值得以改變。
三、組件A和無關(guān)系組件B之間的通信
如果組件之間沒有任何關(guān)系,或者組件嵌套的層次比較深,或者,你為了一些組件能夠訂閱,寫入一些信號(hào),不想讓兩個(gè)組件之間插入一個(gè)組件,而是讓兩個(gè)組件出于獨(dú)立的關(guān)系。對(duì)于時(shí)間系統(tǒng),有兩個(gè)基本操作:
并發(fā)送 send / 觸發(fā) trigger / 發(fā)布 publish / 發(fā)送 dispatch 通知那些想要的組件
1. Event Emitter/Target/Dispatcher
特點(diǎn): 需要一個(gè)指定的訂閱源
// to subscribe otherObiect.addEventListener('clickEvent', function() { alert('click!'); }) // to dispatch this.dispatchEvent('clickEvent');
2. Publish / Subscribe
特點(diǎn): 觸發(fā)的時(shí)候,不需要指定一個(gè)特定的源,使用全局對(duì)象廣播的方式來處理事件
// to subscribe globalBroadcaster.subcribe('clickEvent', function() { alert('cilck!'); }) // to publish globalBroadcaster.publish('clickEvent');
這種方案還有一個(gè)插件可用, 即 PubSubJs;用法如下:
import Pubsub from 'pubsub-js'; ... // to subscribe Pubsub.subscribe('EVENT', (msg, param) => { console.log(msg, param); }); // to publish Pubsub.publish('EVENT', param);
3. Single
特點(diǎn): 與 Event Emitter/Target/Dispatcher
類似,但是不要使用隨機(jī)字符串作為事件觸發(fā)的引用。觸發(fā)事件的每一個(gè)對(duì)象都需要一個(gè)確切的名字,并且在觸發(fā)的時(shí)候,也必須要指定確切的事件
// to subscribe otherObject.clicked.add(function() { alert('click'); }) // to dispatch this.clicked.dispatch();
React 團(tuán)隊(duì)使用的是:js-signals 它基于 Signals 模式,用起來相當(dāng)不錯(cuò)。
事件訂閱與取消
使用React事件的時(shí)候,必須關(guān)注以下兩個(gè)方法:
在 componentDidMount 事件中,等待組件掛載 mounted 完成,再訂閱事件;訂閱的事件需要在組件卸載 componentWillUnmount 的時(shí)候取消事件的訂閱。
因?yàn)榻M件的渲染和銷毀是有 React 來控制的,我們不知道怎么引用他們,所以EventEmitter 模式在處理事件的時(shí)候用處不大,Pub/Sub 模式就好用些,因?yàn)槲覀儾恍枰酪迷谀摹?/p>
ES6策略: yield and js-csp
ES6中有一種傳遞信息的方式,使用生成函數(shù) generators 和 yield 關(guān)鍵字,用法參考以下例子
import csp from 'js-csp'; function* list() { for(var i = 0; i< arguments.length; i++) { yield arguments[i]; } return "done"; } var o = list(1, 2, 3); var cur = o.next; while (!cur.done) { cur = o.next(); console.log(cur); }
結(jié)束語
數(shù)據(jù)在組件中應(yīng)該以什么樣的方式進(jìn)行傳遞取決于組件之間存在什么樣的關(guān)系和當(dāng)時(shí)的業(yè)務(wù)場(chǎng)景需求,大家應(yīng)該根據(jù)項(xiàng)目合理選擇數(shù)據(jù)處理的方案,這很可能減少你大量的代碼量和代碼邏輯。
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com