上一篇文章 玩轉 React(六)- 處理事件 介紹了在 React 中如何處理用戶事件,以及 React 事件機制與原生 DOM 事件的差別和注意的問題,同時也介紹了事件處理函數中 this 的指向問題以及處理的幾種方式及其優缺點。html
你們在閱讀的過程當中有任何爲題能夠給我留言,同時歡迎你們加入玩轉 React 微信羣,個人微信號是 leobaba88
,先加我好友,驗證信息:玩轉 React,而後我會拉你進羣。前端
今天這篇文章要講的內容是關於多個組件之間如何共享數據,或者說是如何通訊的。只有掌握了正確的組件之間通訊的方式,才能在開發交互複雜的前端應用時作到遊刃有餘,所謂正確的方式也就是符合 React 設計理念的方式。使用一個框架時,必定要聽從框架的最佳實踐,人家框架是這樣設計的,你偏要那樣來用,用得不爽還要噴其很差用,那就不該該了。react
如今咱們就來探討下,什麼樣的方式纔是 React 中組件之間通訊的正確方式。redux
在前面的文章中,咱們有說過,React 之因此能勝任大型複雜前端項目的開發,是由於其 單向數據流 這一重要特性,單向數據流能讓視圖更新邏輯變得簡單,從原始的對 DOM 操做變爲對數據操做,簡單了就容易維護。segmentfault
React 組件中數據的流動方向是自頂向下的,也就是說在組件樹中,數據只能從父組件以屬性的方式傳遞到子組件,父組件的數據多是其接收到的屬性,也多是自身的內部狀態。bash
有些同窗這裏可能會比較困惑,說子組件明明能夠經過一個函數屬性將數據傳遞給父組件呀。好多同窗甚至所以搞不明白單向數據流和雙向綁定的差別。其實換個角度考慮一下就清楚不少了,既然「數據傳遞」這個詞區分度不夠大,那就換個區分度比較大的說法。咱們能夠這樣理解,函數屬性是子組件用來通知父組件發生了什麼,它更像是子組件觸發的一個事件,父組件能夠依據業務邏輯來選擇如何處理這個事件,它能夠更新數據後從新傳遞給子組件,也能夠置之不理。微信
函數屬性(或者說事件)在組件之間通訊過程當中是必不可少的,可是切莫讓它影響了你們對單向數據流這一律唸的理解。框架
數據雙向綁定不同,在雙向綁定中父組件將數據傳遞給子組件,子組件修改數據後會將數據回傳同步給父組件,父組件是無條件接受的。這裏就不過多去說哪一個好哪一個差了,有興趣的同窗能夠本身去體會,懶一點的就堅持學習 React 吧。函數
既然 React 中的數據是單向自頂向下傳遞的,那麼符合 React 這一特性的組件通訊方式就顯而易見了。工具
狀態提高的意思是,當組件 A 須要依賴另一個組件 B 的內部狀態,而他們又不是父子關係時,須要將組件 B 的內部狀態提高到他們公共的祖先組件中管理。這樣他們就均可以經過屬性接收到這份數據了。
當組件 B 須要對數據進行變動時,能夠經過函數屬性來通知祖先組件對數據更新,而後從新傳遞給子組件。
有些同窗可能又會迷惑,爲何多個組件之間必需要共用同一份數據,我可不能夠引入一個事件庫,一個組件分發事件,另外一個組件註冊相應的事件來接受數據本身維護。
相似的方案五花八門,會有不少,我認爲這樣作固然是很差的,會有以下問題:
破壞了組件的封裝性,易於複用的組件都是相對獨立的,它只須要定義本身須要的數據和行爲(函數屬性)便可,我不須要誰幫我分發事件。
數據傳遞是不連續的,這樣作會增長項目的複雜性,當項目到必定階段後,對這份數據的依賴就變得千絲萬縷、難以維護了。
相同的數據會有多個副本,須要保證數據同步,在增長項目複雜性的同時也提升了出現BUG的概率。
這是我我的的見解,我也確實有遇到過這種用法,有不一樣意見你們能夠進羣交流。
數據惟一來源是官方推薦的數據共享的原則,也是最符合 React 設計理念,與單向數據流特性相輔相成的,但願你們務必遵照。
Redux 是一個狀態管理庫,它不是專屬於 React 技術棧的,可是跟 React 配合起來至關不錯。
當咱們的前端應用規模較小的時候,咱們能夠不引入任何的狀態管理工具,只須要依據上面說的狀態提高的方式來管理應用狀態便可。爲了讓應用的狀態更直觀,你能夠將跟組件做爲狀態總線,來管理整個應用全部的狀態。並且對於小規模的項目是推薦這樣來作的,沒有必要高射炮打蚊子,過渡設計。
可是當前端應用規模變得比較複雜時,咱們就須要有相似 Redux 這樣一個來專門進行狀態管理的東西了。它的職責以下:
Redux 跟 React 一塊兒來用,更詳細的介紹能夠參考:官方文檔,這裏你們能夠先簡單瞭解下,在後面關於 React 實戰的文章中也會詳細介紹 Redux 的使用。
相似的狀態管理工具還有:MobxJS,感興趣的同窗也能夠了解下。
在 React 中還有一些其餘的與組件間通訊相關的知識,這裏也順便跟你們介紹下。
首先說一下,這是一個不推薦使用的特性,React 官方有明確說明,這是一個實驗性的API,可能會在後面的版本中去掉這個東西。因此我是歷來不用的,呵呵!
context 的做用是啥呢,當你們有過 React 實戰經驗時,很容易遇到這種場景,若是組件的層級組織得不合適,可能會嵌套的很是深,當底層的一個組件須要使用頂層一個組件的數據時,須要經過屬性一層層傳遞下去,很是繁瑣。
context 就是解決這個問題的,只須要在頂層組件中聲明 context,那它的全部子組件能夠經過 this.context 直接獲取獲得。以下實例所示:
import React from 'react';
import PropTypes from 'prop-types';
class Button extends React.Component {
render() {
return (
<button style={{background: this.context.color}}>
{this.props.children}
</button>
);
}
}
Button.contextTypes = {
color: PropTypes.string
};
class Message extends React.Component {
render() {
return (
<div>
{this.props.text} <Button>Delete</Button>
</div>
);
}
}
class MessageList extends React.Component {
getChildContext() {
return {color: "purple"};
}
render() {
const children = this.props.messages.map((message) =>
<Message text={message.text} />
);
return <div>{children}</div>;
}
}
MessageList.childContextTypes = {
color: PropTypes.string
};
複製代碼
實例中,組件層級關係是:MessageList -> Message -> Button。
MessageList 組件中維護一個 color
值用於 Button 組件的背景色,通常狀況下咱們須要將 color 以屬性的方式傳給 Message 組件,再經過屬性傳給 Button 組件。而後在實例中,經過 React 的 context 功能,MessageList 能夠將 color 的值越過 Message 直接傳給 Button。
是否是很方便?確實很方便,可是這會致使數據傳遞不連續,過分使用會使得項目邏輯變得不直觀,增長項目維護的複雜性。
每個 React 組件有一個特殊的屬性 ref
,該屬性的值能夠是一個字符串,也能夠是一個函數。因爲字符串形式的 ref 在內部實現和實際使用中存在諸多問題,官方不推薦使用,並且可能在將來的版本中會移除,因此咱們也不必聊它了,只要你們在看到字符串形式的 ref 屬性時知道也有這種用法就能夠了。
當 ref
屬性值是一個函數時,若是組件是一個 HTML 元素兼容的 React 內部組件時(如:div、img 等),函數接收其對應的原生 DOM 節點做爲參數。**若是組件是一個咱們以類的方式定義的組件時,函數接收該組件類的實例做爲參數。**須要注意的是,若是組件是一個以函數的方式定義的組件,那麼設置爲 ref
值得函數永遠都會接收到一個 null
。
那麼 ref
與組件之間的通訊有什麼關係呢?請看上段文字加粗內容和下面這個實例:
class UserForm extends React.Component {
constructor(props) {
super(props)
this.state = {
name: null,
age: null
}
}
formData() {
return this.state
}
handleFieldChange(e) {
const { name, value } = e.target
this.setState({
[name]: value
})
}
render() {
return (
<div>
<input
type="text"
name="name"
placeholder="Name"
onChange={e => this.handleFieldChange(e)}
/>
<input
type="text"
name="age"
placeholder="Age"
onChange={e => this.handleFieldChange(e)}
/>
</div>
)
}
}
class App extends React.Component {
handleSubmit() {
const formData = this.form.formData()
alert(`formData: ${JSON.stringify(formData)}`)
}
render() {
return (
<div>
<UserForm ref={form => {this.form = form}} />
<button onClick={() => this.handleSubmit()}>Submit</button>
</div>
)
}
}
ReactDOM.render(<App />, document.querySelector('#root'))
複製代碼
演示地址:https://codepen.io/Sarike/pen/OOKYXJ
既然經過 ref
可以獲取子組件的實例,那麼咱們天然能夠調用其成員方法,從而獲取數據。
固然,目前這確實能工做,但絕對不是一種好的方式。由於做爲一個組件,是須要有必定的封裝性的,它應該對外只會承諾我接受什麼樣的屬性,而不會承諾有什麼樣的成員方法。換句話說,若是 JavaScript 的類支持私有成員方法,那麼 React 組件類中的成員方法都應該定義成私有的。
這應該屬於一種 Hack 的使用方式,並且這樣作有悖單向數據流原則。
ref 有它本身的使用場景,這裏只是說明這種方式不適用於組件之間通訊。
雖然囉嗦了這麼多,實際上只但願你們知道一件事情,請使用狀態提高的方式在多個組件之間共享數據,切記維持應用單向數據流和數據惟一來源原則。
文章中有些觀點仁者見仁,有什麼疑惑歡迎留言討論。
很久沒更新了,可是沒有放棄,感謝你們支持。歡迎加我微信好友:leobaba88
,進羣交流。驗證信息:玩轉 React
。
PS:本系列的全部文章將在 segmentfault 和 掘金 同步發佈。
本做品保留全部權利。未得到許可人許可前,不容許他人複製、發行、展覽和表演做品。不容許他人基於該做品創做演繹做品 。