摘要:函數(shù)屬性或者說事件在組件之間通信過程中是必不可少的,但是切莫讓它影響了大家對(duì)單向數(shù)據(jù)流這一概念的理解。這應(yīng)該屬于一種的使用方式,而且這樣做有悖單向數(shù)據(jù)流原則。
上一篇文章 玩轉(zhuǎn) React(六)- 處理事件 介紹了在 React 中如何處理用戶事件,以及 React 事件機(jī)制與原生 DOM 事件的差異和注意的問題,同時(shí)也介紹了事件處理函數(shù)中 this 的指向問題以及處理的幾種方式及其優(yōu)缺點(diǎn)。
大家在閱讀的過程中有任何為題可以給我留言,同時(shí)歡迎大家加入玩轉(zhuǎn) React 微信群,我的微信號(hào)是 leobaba88,先加我好友,驗(yàn)證信息:玩轉(zhuǎn) React,然后我會(huì)拉你進(jìn)群。
今天這篇文章要講的內(nèi)容是關(guān)于多個(gè)組件之間如何共享數(shù)據(jù),或者說是如何通信的。只有掌握了正確的組件之間通信的方式,才能在開發(fā)交互復(fù)雜的前端應(yīng)用時(shí)做到游刃有余,所謂正確的方式也就是符合 React 設(shè)計(jì)理念的方式。使用一個(gè)框架時(shí),一定要遵從框架的最佳實(shí)踐,人家框架是這樣設(shè)計(jì)的,你偏要那樣來用,用得不爽還要噴其不好用,那就不應(yīng)該了。
內(nèi)容摘要React 中的數(shù)據(jù)是單向自頂向下傳遞的。
單向數(shù)據(jù)流與雙向綁定的差異。
最符合 React 理念的組件之間共享數(shù)據(jù)的方式。
數(shù)據(jù)唯一來源原則。
一些不好的方式。
先跟 Redux 打個(gè)招呼。
其他一些關(guān)于組件間通信的內(nèi)容(context、ref)。
組件之間通信的最佳方式現(xiàn)在我們就來探討下,什么樣的方式才是 React 中組件之間通信的正確方式。
在前面的文章中,我們有說過,React 之所以能勝任大型復(fù)雜前端項(xiàng)目的開發(fā),是因?yàn)槠?單向數(shù)據(jù)流 這一重要特性,單向數(shù)據(jù)流能讓視圖更新邏輯變得簡(jiǎn)單,從原始的對(duì) DOM 操作變?yōu)閷?duì)數(shù)據(jù)操作,簡(jiǎn)單了就容易維護(hù)。
React 組件中數(shù)據(jù)的流動(dòng)方向是自頂向下的,也就是說在組件樹中,數(shù)據(jù)只能從父組件以屬性的方式傳遞到子組件,父組件的數(shù)據(jù)可能是其接收到的屬性,也可能是自身的內(nèi)部狀態(tài)。
有些同學(xué)這里可能會(huì)比較困惑,說子組件明明可以通過一個(gè)函數(shù)屬性將數(shù)據(jù)傳遞給父組件呀。好多同學(xué)甚至因此搞不明白單向數(shù)據(jù)流和雙向綁定的差異。其實(shí)換個(gè)角度考慮一下就清楚很多了,既然“數(shù)據(jù)傳遞”這個(gè)詞區(qū)分度不夠大,那就換個(gè)區(qū)分度比較大的說法。我們可以這樣理解,函數(shù)屬性是子組件用來通知父組件發(fā)生了什么,它更像是子組件觸發(fā)的一個(gè)事件,父組件可以依據(jù)業(yè)務(wù)邏輯來選擇如何處理這個(gè)事件,它可以更新數(shù)據(jù)后重新傳遞給子組件,也可以置之不理。
函數(shù)屬性(或者說事件)在組件之間通信過程中是必不可少的,但是切莫讓它影響了大家對(duì)單向數(shù)據(jù)流這一概念的理解。
數(shù)據(jù)雙向綁定不一樣,在雙向綁定中父組件將數(shù)據(jù)傳遞給子組件,子組件修改數(shù)據(jù)后會(huì)將數(shù)據(jù)回傳同步給父組件,父組件是無條件接受的。這里就不過多去說哪個(gè)好哪個(gè)差了,有興趣的同學(xué)可以自己去體會(huì),懶一點(diǎn)的就堅(jiān)持學(xué)習(xí) React 吧。
狀態(tài)提升(Lifting State Up)既然 React 中的數(shù)據(jù)是單向自頂向下傳遞的,那么符合 React 這一特性的組件通信方式就顯而易見了。
狀態(tài)提升的意思是,當(dāng)組件 A 需要依賴另外一個(gè)組件 B 的內(nèi)部狀態(tài),而他們又不是父子關(guān)系時(shí),需要將組件 B 的內(nèi)部狀態(tài)提升到他們公共的祖先組件中管理。這樣他們就都可以通過屬性接收到這份數(shù)據(jù)了。
當(dāng)組件 B 需要對(duì)數(shù)據(jù)進(jìn)行變更時(shí),可以通過函數(shù)屬性來通知祖先組件對(duì)數(shù)據(jù)更新,然后重新傳遞給子組件。
唯一數(shù)據(jù)來源(Single source of truth)有些同學(xué)可能又會(huì)迷惑,為什么多個(gè)組件之間必須要共用同一份數(shù)據(jù),我可不可以引入一個(gè)事件庫,一個(gè)組件分發(fā)事件,另一個(gè)組件注冊(cè)相應(yīng)的事件來接受數(shù)據(jù)自己維護(hù)。
類似的方案五花八門,會(huì)有很多,我認(rèn)為這樣做當(dāng)然是不好的,會(huì)有如下問題:
破壞了組件的封裝性,易于復(fù)用的組件都是相對(duì)獨(dú)立的,它只需要定義自己需要的數(shù)據(jù)和行為(函數(shù)屬性)即可,我不需要誰幫我分發(fā)事件。
數(shù)據(jù)傳遞是不連續(xù)的,這樣做會(huì)增加項(xiàng)目的復(fù)雜性,當(dāng)項(xiàng)目到一定階段后,對(duì)這份數(shù)據(jù)的依賴就變得千絲萬縷、難以維護(hù)了。
相同的數(shù)據(jù)會(huì)有多個(gè)副本,需要保證數(shù)據(jù)同步,在增加項(xiàng)目復(fù)雜性的同時(shí)也提高了出現(xiàn)BUG的幾率。
這是我個(gè)人的看法,我也確實(shí)有遇到過這種用法,有不同意見大家可以進(jìn)群交流。
數(shù)據(jù)唯一來源是官方推薦的數(shù)據(jù)共享的原則,也是最符合 React 設(shè)計(jì)理念,與單向數(shù)據(jù)流特性相輔相成的,希望大家務(wù)必遵守。
ReduxRedux 是一個(gè)狀態(tài)管理庫,它不是專屬于 React 技術(shù)棧的,但是跟 React 配合起來相當(dāng)不錯(cuò)。
當(dāng)我們的前端應(yīng)用規(guī)模較小的時(shí)候,我們可以不引入任何的狀態(tài)管理工具,只需要依據(jù)上面說的狀態(tài)提升的方式來管理應(yīng)用狀態(tài)即可。為了讓應(yīng)用的狀態(tài)更直觀,你可以將跟組件作為狀態(tài)總線,來管理整個(gè)應(yīng)用所有的狀態(tài)。而且對(duì)于小規(guī)模的項(xiàng)目是推薦這樣來做的,沒有必要高射炮打蚊子,過渡設(shè)計(jì)。
但是當(dāng)前端應(yīng)用規(guī)模變得比較復(fù)雜時(shí),我們就需要有類似 Redux 這樣一個(gè)來專門進(jìn)行狀態(tài)管理的東西了。它的職責(zé)如下:
維護(hù)一個(gè)數(shù)據(jù)倉庫(store)管理整個(gè)應(yīng)用的狀態(tài)(state),確保數(shù)據(jù)的唯一來源。
可以通過 dispatch 方法分發(fā)一個(gè) action,來通知 Redux 需要對(duì)數(shù)據(jù)進(jìn)行變更。
Redux 接收到 action 后可以依據(jù) action 的類型對(duì) state 進(jìn)行相應(yīng)的修改。
數(shù)據(jù)跟新后 Redux 會(huì)觸發(fā)注冊(cè)的監(jiān)聽器(如:更新組件屬性),完成視圖更新。
Redux 跟 React 一起來用,更詳細(xì)的介紹可以參考:官方文檔,這里大家可以先簡(jiǎn)單了解下,在后面關(guān)于 React 實(shí)戰(zhàn)的文章中也會(huì)詳細(xì)介紹 Redux 的使用。
類似的狀態(tài)管理工具還有:MobxJS,感興趣的同學(xué)也可以了解下。
關(guān)于組件通信的其他內(nèi)容在 React 中還有一些其他的與組件間通信相關(guān)的知識(shí),這里也順便跟大家介紹下。
context首先說一下,這是一個(gè)不推薦使用的特性,React 官方有明確說明,這是一個(gè)實(shí)驗(yàn)性的API,可能會(huì)在后面的版本中去掉這個(gè)東西。所以我是從來不用的,呵呵!
context 的作用是啥呢,當(dāng)大家有過 React 實(shí)戰(zhàn)經(jīng)驗(yàn)時(shí),很容易遇到這種場(chǎng)景,如果組件的層級(jí)組織得不合適,可能會(huì)嵌套的非常深,當(dāng)?shù)讓拥囊粋€(gè)組件需要使用頂層一個(gè)組件的數(shù)據(jù)時(shí),需要通過屬性一層層傳遞下去,非常繁瑣。
context 就是解決這個(gè)問題的,只需要在頂層組件中聲明 context,那它的所有子組件可以通過 this.context 直接獲取得到。如下實(shí)例所示:
import React from "react"; import PropTypes from "prop-types"; class Button extends React.Component { render() { return ( ); } } Button.contextTypes = { color: PropTypes.string }; class Message extends React.Component { render() { return ({this.props.text}); } } class MessageList extends React.Component { getChildContext() { return {color: "purple"}; } render() { const children = this.props.messages.map((message) =>); return {children}; } } MessageList.childContextTypes = { color: PropTypes.string };
實(shí)例中,組件層級(jí)關(guān)系是:MessageList -> Message -> Button。
MessageList 組件中維護(hù)一個(gè) color 值用于 Button 組件的背景色,一般情況下我們需要將 color 以屬性的方式傳給 Message 組件,再通過屬性傳給 Button 組件。然后在實(shí)例中,通過 React 的 context 功能,MessageList 可以將 color 的值越過 Message 直接傳給 Button。
是不是很方便?確實(shí)很方便,但是這會(huì)導(dǎo)致數(shù)據(jù)傳遞不連續(xù),過度使用會(huì)使得項(xiàng)目邏輯變得不直觀,增加項(xiàng)目維護(hù)的復(fù)雜性。
ref每一個(gè) React 組件有一個(gè)特殊的屬性 ref,該屬性的值可以是一個(gè)字符串,也可以是一個(gè)函數(shù)。由于字符串形式的 ref 在內(nèi)部實(shí)現(xiàn)和實(shí)際使用中存在諸多問題,官方不推薦使用,而且可能在未來的版本中會(huì)移除,所以我們也沒必要聊它了,只要大家在看到字符串形式的 ref 屬性時(shí)知道也有這種用法就可以了。
當(dāng) ref 屬性值是一個(gè)函數(shù)時(shí),如果組件是一個(gè) HTML 元素兼容的 React 內(nèi)部組件時(shí)(如:div、img 等),函數(shù)接收其對(duì)應(yīng)的原生 DOM 節(jié)點(diǎn)作為參數(shù)。如果組件是一個(gè)我們以類的方式定義的組件時(shí),函數(shù)接收該組件類的實(shí)例作為參數(shù)。需要注意的是,如果組件是一個(gè)以函數(shù)的方式定義的組件,那么設(shè)置為 ref 值得函數(shù)永遠(yuǎn)都會(huì)接收到一個(gè) null。
那么 ref 與組件之間的通信有什么關(guān)系呢?請(qǐng)看上段文字加粗內(nèi)容和下面這個(gè)實(shí)例:
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 (this.handleFieldChange(e)} /> this.handleFieldChange(e)} />) } } class App extends React.Component { handleSubmit() { const formData = this.form.formData() alert(`formData: ${JSON.stringify(formData)}`) } render() { return () } } ReactDOM.render({this.form = form}} /> , document.querySelector("#root"))
演示地址:https://codepen.io/Sarike/pen...
既然通過 ref 能夠獲取子組件的實(shí)例,那么我們自然可以調(diào)用其成員方法,從而獲取數(shù)據(jù)。
當(dāng)然,目前這確實(shí)能工作,但絕對(duì)不是一種好的方式。因?yàn)樽鳛橐粋€(gè)組件,是需要有一定的封裝性的,它應(yīng)該對(duì)外只會(huì)承諾我接受什么樣的屬性,而不會(huì)承諾有什么樣的成員方法。換句話說,如果 JavaScript 的類支持私有成員方法,那么 React 組件類中的成員方法都應(yīng)該定義成私有的。
這應(yīng)該屬于一種 Hack 的使用方式,而且這樣做有悖單向數(shù)據(jù)流原則。
ref 有它自己的使用場(chǎng)景,這里只是說明這種方式不適用于組件之間通信。
總結(jié)雖然啰嗦了這么多,實(shí)際上只希望大家知道一件事情,請(qǐng)使用狀態(tài)提升的方式在多個(gè)組件之間共享數(shù)據(jù),切記維持應(yīng)用單向數(shù)據(jù)流和數(shù)據(jù)唯一來源原則。
文章中有些觀點(diǎn)仁者見仁,有什么疑惑歡迎留言討論。
好久沒更新了,但是沒有放棄,感謝大家支持。歡迎加我微信好友:leobaba88,進(jìn)群交流。驗(yàn)證信息:玩轉(zhuǎn) React。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/90330.html
摘要:本人計(jì)劃編寫一個(gè)針對(duì)中初級(jí)前端開發(fā)者學(xué)習(xí)的系列教程玩轉(zhuǎn)。使用的原因是新的語言規(guī)范開發(fā)效率更高代碼更優(yōu)雅,尤其是基于開發(fā)的項(xiàng)目。其次也是目前特別流行的一個(gè)前端框架,截止目前,上有將近萬,國(guó)內(nèi)一二線互聯(lián)網(wǎng)公司都有深度依賴開發(fā)的項(xiàng)目。 本人計(jì)劃編寫一個(gè)針對(duì)中初級(jí)前端開發(fā)者學(xué)習(xí) React 的系列教程 - 《玩轉(zhuǎn) React》。 文章更新頻率:每周 1 ~ 2 篇。 目錄 玩轉(zhuǎn) React(...
摘要:類組件中的增加學(xué)習(xí)成本,類組件在基于現(xiàn)有工具的優(yōu)化上存在些許問題。由于業(yè)務(wù)變動(dòng),函數(shù)組件不得不改為類組件等等。那么可愛的各位看官,還不趕緊使用起來在線示例點(diǎn)我版本基礎(chǔ)入門項(xiàng)目錄像教程 視圖與業(yè)務(wù),好一對(duì)冤家 業(yè)務(wù)型model model是需要精心的設(shè)計(jì)和合理的劃分的,這是我們之前開發(fā)大型的redux+react單頁面應(yīng)用,大家都認(rèn)同的真理,同樣的,在react-control-cent...
摘要:這也就是所謂的單向數(shù)據(jù)流,在這種開發(fā)方式下,會(huì)讓你更新視圖的邏輯非常清晰簡(jiǎn)單,哪怕你的前端交互很復(fù)雜,也不至于讓你的代碼那么容易變成一坨。就是在前端開發(fā)過程中,要善于觀察和抽象。 這是《玩轉(zhuǎn) React》系列的第二篇。在該篇中,我們來了解下,React 的出現(xiàn)到底給我們的開發(fā)方式帶來了什么樣的變化。 我的感觸可以用一個(gè)字來形容,爽!主要爽在以下兩個(gè)方面。 視圖是數(shù)據(jù)的映射(單向數(shù)據(jù)流)...
摘要:屬性是一個(gè)組件的外部輸入。只會(huì)在開發(fā)模式下進(jìn)行屬性類型檢查,當(dāng)代碼進(jìn)行生產(chǎn)發(fā)布后,為了減少額外的性能開銷,類型檢查將會(huì)被略過。某個(gè)類的實(shí)例枚舉,屬性值必須為其中的某一個(gè)值。屬性為一個(gè)數(shù)組,且數(shù)組中的元素必須符合指定類型。 在第二篇文章 《新型前端開發(fā)方式》 中有說到 React 有很爽的一點(diǎn)就是給我們一種創(chuàng)造 HTML 標(biāo)簽的能力,那么今天這篇文章就詳細(xì)講解下 React 是如何提供這...
摘要:屬性是一個(gè)組件的外部輸入。只會(huì)在開發(fā)模式下進(jìn)行屬性類型檢查,當(dāng)代碼進(jìn)行生產(chǎn)發(fā)布后,為了減少額外的性能開銷,類型檢查將會(huì)被略過。某個(gè)類的實(shí)例枚舉,屬性值必須為其中的某一個(gè)值。屬性為一個(gè)數(shù)組,且數(shù)組中的元素必須符合指定類型。 在第二篇文章 《新型前端開發(fā)方式》 中有說到 React 有很爽的一點(diǎn)就是給我們一種創(chuàng)造 HTML 標(biāo)簽的能力,那么今天這篇文章就詳細(xì)講解下 React 是如何提供這...
閱讀 2308·2021-11-23 09:51
閱讀 3784·2021-10-20 13:49
閱讀 1780·2021-09-06 15:13
閱讀 1882·2021-09-06 15:02
閱讀 3358·2021-09-02 15:11
閱讀 962·2019-08-29 15:37
閱讀 1804·2019-08-29 13:24
閱讀 2325·2019-08-29 11:28