單向數據流與組件間通信
上文我們已經講述過,react 單向數據流的原理和簡單模擬實現。結合上文中的代碼,我們來進行這節面試題的講解: react中的組件間通信。
那么,首先我們把看上文中的原生js代碼:
function child(props){
this.props = props;
}
function parent(props){
this.props = props
this.state = '這是父函數的一個狀態'
this.childNodes = new child(this.state);
}
console.log(new parent('這是一個屬性'));
ok,經過運行,我們發現打印結果的結構 和 react父子組件的關系結構是極為類似的,子函數通過props形參接受到父函數的state。
這個好理解吧?那么在說react組件通信之前,我們基於原生繼續模擬父子組件間的通信。
如上文代碼所示,我們已經實現了父函數通過props這個形參向子函數傳遞數據——是的,相對而言,在react中,父組件通過props(規定了只能通過props傳遞及獲取)向子組件進行數據傳遞。換句話講,props這個實例屬性,表面唯一的作用就是參數傳值…
那么,我們接來下要思考一個問題,如何在上述代碼中實現 子函數向父函數傳遞數據?
如果我們把函數玩的夠熟練,那么很容易會思考到以下實現方式:
function child(props){
this.data = '我是子函數中的data'
props.getChildData(this.data);
this.props = props;
}
function parent(props){
this.props = props;
this.dataFromChild = null;
this.getChildData = (data)=>{
console.log('這個函數被運行了,我們拿到了傳過來的參數:'+data);
this.dataFromChild = data;
}
this.childNodes = new child({
getChildData:this.getChildData
});
}
console.log(new parent('這是一個屬性'));
(別偷懶!f12,復制上述代碼到控制台運行!)
好的,簡單說下上述代碼做出的更改:
1 父函數中定義了一個函數,通過參數對象(實則就是props啊)傳遞給了子函數;
2 這個函數一旦被執行,會把參數data賦值給父函數中的實例屬性data;
3 子函數通過props接收到了這個函數,運行了它,並且把自己的data作為參數傳遞了進去;
ok,那么我們經過運行,發現控制台打印的內容:

哦!!!於是我們明白了,我們寫原生js的時候,就是通過:
在父函數中定義方法,通過參數傳遞給子函數,子函數調用這個方法並且把自己數據作為參數,那么父函數就可以通過形參拿到了~
什么?有點繞?那么說的再簡單點:
父函數的方法傳遞給子函數,被子函數傳參調用。
那么趕緊用內存不足的大腦思考思考react中是不是也可以啊?
class Child extends React.Component{
data = '我是子組件中的data'
render(){
this.props.getChildData(this.data);
return <div>我是Child組件</div>
}
}
class Parent extends React.Component{
childData=null
getChildData = (data)=>{
this.childData = data;
console.log(data);
}
render(){
return <Child getChildData = { this.getChildData } />
}
}
console.log(<Parent />)
上面的代碼夠簡練了吧(比中指)!
那么我們運行上述代碼發現——乖乖,真的拿到了~~這不還是一樣嗎?我們玩的不是react嗎?怎么成原生js 了啊?啥啊誰啊我在哪啊??

實則所有單向數據流的js框架都可以通過上述的方式進行子組件向父組件的通信。
非常好,現在我們來整理一下react的父子組件間通信規則:
*父傳子: 父組件通過props傳參唄……*
*子傳父 : 父組件通過props傳參唄……*
啥? 我寫重了?
(哼哼一聲我露出了鄙視的微笑,並在下面多寫了一行)
***子傳父完整版: 父組件中定義函數,通過props傳遞給子組件,子組件調用這個函數並傳參。***
嗯…經過思考,我們得出了這樣一個結論: 子傳父這樣的逆向通信,實際也是符合單向數據流的概念。無非就是把函數當做參數傳遞下去而已啊!!!(重點是這句啊~~~react中的逆向通信的方式和單向數據流完全不沖突啊~)
子傳父(逆向通信)的其他方式
除開上面講過的 父組件中定義函數傳遞給子組件並被其調用 這種js通用方式,在react中我們還可以怎樣玩?
在Vue,React中都有ref 這樣一個特殊的實例屬性。
react中 ref 具備兩種作用:
1 標記dom的話,能獲取dom實例;
2 標記子組件的話,能獲取子組件實例的所有數據(包括子組件的props,state,定義的方法,實例…)。
那么也就是說,父組件通過this.ref就拿到子組件的任何數據了…就這么簡單啊。
當然,依照react的尿性,文檔中特意提到了一點:不要輕易用這玩意啊,危險啊,別沖動啊小伙子們~
事實上,經過react的版本變化,ref賦值目前有三種方式 : 字符串、回調、 CreateRef() //這個是16.3后的新增api
迭代維護做的都很到位啊~ 憑啥只用在表單控件里啊~
當然,關於ref的花式玩法我們會在本系列后面內容講到。
遠親組件的通信
其實無論做react還是vue或者其他項目,我們必須要明確,寫的還是js。(這也是為什么我一直把原生和react放在一起講)
那么遠親組件的通信,我們即時對react毫無了解,也能至少說出一萬種方式…
例如:
1 通過緩存 : 組件A把數據存到緩存中,組件B就可以從中取出;
2 通過url : 通過location對象拿到…;
3 通過與后端配合: 組件A把數據扔到接口里去,組件B可以從中拿到…;
以上方式,我稱之為通過第三方媒介的形式。
(插一句: 有木有覺得redux中store共享跟session級別緩存很類似?)
也就是說,遠親組件的通信,我們拋開react去思考得出一個結論:
——通過第三方媒介作為一個存儲地點,實現數據共享。
ok我們把話題轉回來。react中,同系遠親的話(太太爺爺到重重孫子),完全可以通過context嘛(上一篇有講,后面也會出context的專章)…
至於不是很遠的關系, 利用props一層一層往內傳啊。 (例如爺爺傳到孫子)
逆向: 子調用爺爺的函數並傳參就實現了子傳爺爺;
同級 : 子1傳父,父傳子2 ;
很遠關系: 1 context(自己實現試試啊~) 2 通過第三方媒介共享( 緩存,url,服務端,以及后面我們會講到的redux,react-redux,dva等全局狀態存儲管理插件);
我們發現,就單單react的話,傳來傳去還是 父傳子 子傳父啊……最多特殊情況用到context(無論多遠的關系,最終肯定有個統一的祖宗啊<App ==!/>)而已啊~
總結
這章講述了react的基礎必面的面試題:
如何在react項目中實現組件間的通信。
也可能會引發一些react與項目與原生的思考(這是我想要的)。
如果本章內容對您有幫助,請點個推薦哦~
