摘要:的單向數(shù)據(jù)流也稱為單向綁定使所有的事務(wù)更加模塊化也更加快速。第五步添加反向數(shù)據(jù)流到目前為止,我們已經(jīng)構(gòu)建了一個(gè)應(yīng)用程序,可以根據(jù)和正確地呈現(xiàn)在層次結(jié)構(gòu)中。傳遞的回調(diào)將調(diào)用,并且應(yīng)用程序?qū)⒈桓隆?/p>
本文是對(duì)React官網(wǎng)《Thinking in React》一文的翻譯,通過(guò)這篇文章,React團(tuán)隊(duì)向開(kāi)發(fā)者們介紹了應(yīng)該如果去構(gòu)思一個(gè)web應(yīng)用,為今后使用React進(jìn)行web app的構(gòu)建,打下基礎(chǔ)。 以下是正文。在我們團(tuán)隊(duì)看來(lái),React是使用JavaScript構(gòu)建大型、快速的Web apps的首選方式。它已經(jīng)在Facebook和Instagram項(xiàng)目中,表現(xiàn)出了非常好的可擴(kuò)展性。
能夠按照構(gòu)建的方式來(lái)思考web app的實(shí)現(xiàn),是React眾多優(yōu)點(diǎn)之一。在這篇文章中,我們將引導(dǎo)你進(jìn)行使用React構(gòu)建可搜索產(chǎn)品數(shù)據(jù)表的思考過(guò)程。
從設(shè)計(jì)稿開(kāi)始想象一下,我們已經(jīng)有了一個(gè)JSON API和來(lái)自設(shè)計(jì)師的設(shè)計(jì)稿。如下圖所示:
JSON API返回的數(shù)據(jù)如下所示:
[ {category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"}, {category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"}, {category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"}, {category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"}, {category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"}, {category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"} ];第一步:將UI分解為組件并分析層級(jí)結(jié)構(gòu)
我們要做的第一件事就是給設(shè)計(jì)稿中的每個(gè)組件(和子組件)畫(huà)框,并給它們起名字。如果你正在和一個(gè)設(shè)計(jì)師合作,他可能已經(jīng)幫你完成了這一步。他的Photoshop圖層名稱可能最終會(huì)成為你的React組件名稱!
但我們?cè)趺粗雷约旱慕M件應(yīng)該是什么?只需要使用一些通用的技巧來(lái)決定是否應(yīng)該創(chuàng)建一個(gè)新的函數(shù)或?qū)ο?。其中一個(gè)技巧叫做:單一責(zé)任原則。就是說(shuō),在理想情況下,一個(gè)組件應(yīng)該只用來(lái)完成一件事。若非如此,則應(yīng)該考慮將其分解成更小的子組件。
我們經(jīng)常會(huì)向用戶展示JSON數(shù)據(jù)模型,那么你應(yīng)該會(huì)發(fā)現(xiàn),如果模型構(gòu)建正確,那么你的UI(以及組件結(jié)構(gòu))應(yīng)該能夠很好地映射數(shù)據(jù)模型。這是因?yàn)閁I和數(shù)據(jù)模型傾向于遵循相同的信息架構(gòu),這意味著將UI分解為組件的工作通常是微不足道的?,F(xiàn)在我們把它分解成映射數(shù)據(jù)模型的組件如下:
現(xiàn)在我們的示例應(yīng)用中有了五個(gè)組件,而且我們將每個(gè)組件代表的數(shù)據(jù)用斜體表示如下:
FilterableProductTable (橘黃色):包含整個(gè)示例的組件
SearchBar (藍(lán)色):接收所有的用戶輸入
ProductTable (綠色):根據(jù)用戶輸入顯示和過(guò)濾數(shù)據(jù)集
ProductCategoryRow (綠寶石色):顯示分類頭
ProductRow (紅色):每行顯示一條商品數(shù)據(jù)
細(xì)心的你會(huì)發(fā)現(xiàn),在ProductTable中,表頭(包含名稱和價(jià)格標(biāo)簽)不是一個(gè)組件。這是一個(gè)偏好的問(wèn)題,有兩個(gè)方面的論點(diǎn)。在這個(gè)例子中,我們將其作為ProductTable組件的一部分,因?yàn)樗?b>ProductTable負(fù)責(zé)渲染的數(shù)據(jù)集的一部分。但是,如果這個(gè)頭部變得很復(fù)雜(比如我們要支持排序),那么將其設(shè)置為ProductTableHeader這樣的組件肯定會(huì)更好一些。
現(xiàn)在我們已經(jīng)確定了設(shè)計(jì)稿中的組件,下一步我們要給這些組件安排層次結(jié)構(gòu)。這其實(shí)很容易:出現(xiàn)在一個(gè)組件中的組件應(yīng)該在層次結(jié)構(gòu)中顯示為一個(gè)子組件:
FilterableProductTable
SearchBar
ProductTable
ProductCategoryRow
ProductRow
第二步:用React構(gòu)建一個(gè)靜態(tài)版本class ProductCategoryRow extends React.Component { render() { const category = this.props.category; return (); } } class ProductRow extends React.Component { render() { const product = this.props.product; const name = product.stocked ? product.name : {product.name} ; return ( {category} ); } } class ProductTable extends React.Component { render() { const rows = []; let lastCategory = null; this.props.products.forEach((product) => { if (product.category !== lastCategory) { rows.push( {name} {product.price} ); } rows.push( ); lastCategory = product.category; }); return (
Name | Price |
---|
現(xiàn)在我們已經(jīng)有了組件層次結(jié)構(gòu),接下來(lái)可以實(shí)現(xiàn)應(yīng)用程序了。最初的方案是構(gòu)建一個(gè)使用數(shù)據(jù)模型渲染UI但不具有交互性的版本。最好將靜態(tài)版本和添加交互性進(jìn)行解耦,因?yàn)闃?gòu)建一個(gè)靜態(tài)的版本需要大量的輸入?yún)s不需要思考,而增加交互性需要大量的思考而不需要很多輸入。我們一會(huì)兒會(huì)知道為什么。
要構(gòu)建渲染數(shù)據(jù)模型的靜態(tài)版本,需要構(gòu)建可復(fù)用其他組件并使用props傳遞數(shù)據(jù)的組件。props是一種將數(shù)據(jù)從父組件傳遞給子組件的方式。如果你熟悉state的概念,請(qǐng)不要使用state來(lái)構(gòu)建這個(gè)靜態(tài)版本。state只為實(shí)現(xiàn)交互性而保留,即隨時(shí)間變化的數(shù)據(jù)。由于這是應(yīng)用程序的靜態(tài)版本,所以暫時(shí)不需要它。
你的構(gòu)建過(guò)程可以自上而下或自下而上。也就是說(shuō),你可以從構(gòu)建層次較高的組件(即FilterableProductTable)開(kāi)始或較低的組件(ProductRow開(kāi)始)。在簡(jiǎn)單的例子中,自上而下通常比較容易,而在大型項(xiàng)目中,自下而上更容易而且更易于編寫(xiě)測(cè)試用例。
在這一步的最后,你會(huì)有一個(gè)可重用組件的庫(kù)來(lái)渲染你的數(shù)據(jù)模型。這些組件只會(huì)有render()方法,因?yàn)檫@是你的應(yīng)用程序的靜態(tài)版本。層次結(jié)構(gòu)頂部的組件(FilterableProductTable)將把你的數(shù)據(jù)模型作為一個(gè)prop。如果你對(duì)基礎(chǔ)數(shù)據(jù)模型進(jìn)行更改并再次調(diào)用ReactDOM.render(),則UI將會(huì)更新。這就很容易看到用戶界面是如何更新以及在哪里進(jìn)行更改了,因?yàn)闆](méi)有任何復(fù)雜的事情發(fā)生。 React的單向數(shù)據(jù)流(也稱為單向綁定)使所有的事務(wù)更加模塊化也更加快速。
第三步:確定UI狀態(tài)的最?。ǖ暾┍硎拘问?/b>為了使你的UI具有交互性,需要能夠觸發(fā)對(duì)基礎(chǔ)數(shù)據(jù)模型的更改。 React使用state讓這一切變得簡(jiǎn)單。要正確構(gòu)建應(yīng)用程序,首先需要考慮應(yīng)用程序需要的最小可變狀態(tài)集。這里的關(guān)鍵是:不要重復(fù)自己。找出應(yīng)用程序需要的狀態(tài)的絕對(duì)最小表示,并計(jì)算需要的其他所有內(nèi)容。例如,如果你正在創(chuàng)建一個(gè)TODO列表,只需要保存一個(gè)TODO項(xiàng)目的數(shù)組;不要為計(jì)數(shù)保留一個(gè)多帶帶的狀態(tài)變量。相反,當(dāng)你要渲染TODO數(shù)量時(shí),只需取TODO項(xiàng)目數(shù)組的長(zhǎng)度即可。
考慮我們示例應(yīng)用程序中的所有數(shù)據(jù)。我們有:
產(chǎn)品的原始列表
用戶輸入的搜索文本
復(fù)選框的值
過(guò)濾的產(chǎn)品列表
我們來(lái)看看每一個(gè)是哪一個(gè)state。這里有關(guān)于每條數(shù)據(jù)的三個(gè)問(wèn)題:
是通過(guò)props從父組件傳入的嗎?如果是,那可能不是state。
它是否保持不變?如果是,那可能不是state。
你能基于組件中的任何其他state或props來(lái)計(jì)算它嗎?如果是,那不是state。
原來(lái)的產(chǎn)品清單是作為props傳入的,所以這不是state。搜索文本和復(fù)選框似乎是state,因?yàn)樗鼈冸S著時(shí)間而改變,不能從任何東西計(jì)算。最后,產(chǎn)品的過(guò)濾列表不是state,因?yàn)樗梢酝ㄟ^(guò)將產(chǎn)品的原始列表與復(fù)選框的搜索文本和值組合來(lái)計(jì)算得到。
所以最后,我們的states是:
用戶輸入的搜索文本
復(fù)選框的值
第四步: 確定你的state需要放置在什么地方class ProductCategoryRow extends React.Component { render() { const category = this.props.category; return (); } } class ProductRow extends React.Component { render() { const product = this.props.product; const name = product.stocked ? product.name : {product.name} ; return ( {category} ); } } class ProductTable extends React.Component { render() { const filterText = this.props.filterText; const inStockOnly = this.props.inStockOnly; const rows = []; let lastCategory = null; this.props.products.forEach((product) => { if (product.name.indexOf(filterText) === -1) { return; } if (inStockOnly && !product.stocked) { return; } if (product.category !== lastCategory) { rows.push( {name} {product.price} ); } rows.push( ); lastCategory = product.category; }); return (
Name | Price |
---|
現(xiàn)在我們已經(jīng)確定了最小的一組應(yīng)用程序state。接下來(lái),我們需要確定哪個(gè)組件會(huì)改變或擁有這個(gè)state。
請(qǐng)記?。簲?shù)據(jù)在React的組件層次結(jié)構(gòu)中是單向流動(dòng)的。它可能不清楚哪個(gè)組件應(yīng)該擁有什么狀態(tài)。這通常是新手理解的最具挑戰(zhàn)性的部分,所以請(qǐng)按照以下步驟解決:
對(duì)于你的應(yīng)用程序中的每一個(gè)state:
確定基于該state渲染某些內(nèi)容的每個(gè)組件。
找到一個(gè)共同的擁有者組件(一個(gè)在所有需要該state的層次結(jié)構(gòu)組件之上的組件)。
無(wú)論是共同所有者,還是高層次的其他組成部分,都應(yīng)該擁有這個(gè)state。
如果你無(wú)法找到一個(gè)有意義的組件,那么只好創(chuàng)建一個(gè)新的組件來(lái)保存state,并將其添加到公共所有者組件上方的層次結(jié)構(gòu)中的某個(gè)位置。
讓我們來(lái)看看我們的應(yīng)用程序的這個(gè)策略:
ProductTable需要根據(jù)狀態(tài)過(guò)濾產(chǎn)品列表,而SearchBar需要顯示搜索文本和檢查狀態(tài)。
通用所有者組件是FilterableProductTable。
從概念上講,過(guò)濾器文本和選中的值存在于FilterableProductTable中是有意義的
酷,所以我們已經(jīng)決定,我們的state存活在FilterableProductTable中。首先,將一個(gè)實(shí)例屬性this.state = {filterText:"",inStockOnly:false}添加到FilterableProductTable的構(gòu)造函數(shù)中,以反映應(yīng)用程序的初始狀態(tài)。然后,將filterText和inStockOnly作為prop傳遞給ProductTable和SearchBar。最后,使用這些props來(lái)篩選ProductTable中的行,并在SearchBar中設(shè)置表單域的值。
你可以看到你的應(yīng)用程序的行為了:設(shè)置filterText為“ball”,并刷新你的應(yīng)用程序。你將看到數(shù)據(jù)表已正確更新。
第五步:添加反向數(shù)據(jù)流class ProductCategoryRow extends React.Component { render() { const category = this.props.category; return (); } } class ProductRow extends React.Component { render() { const product = this.props.product; const name = product.stocked ? product.name : {product.name} ; return ( {category} ); } } class ProductTable extends React.Component { render() { const filterText = this.props.filterText; const inStockOnly = this.props.inStockOnly; const rows = []; let lastCategory = null; this.props.products.forEach((product) => { if (product.name.indexOf(filterText) === -1) { return; } if (inStockOnly && !product.stocked) { return; } if (product.category !== lastCategory) { rows.push( {name} {product.price} ); } rows.push( ); lastCategory = product.category; }); return (
Name | Price |
---|
到目前為止,我們已經(jīng)構(gòu)建了一個(gè)應(yīng)用程序,可以根據(jù)props和state正確地呈現(xiàn)在層次結(jié)構(gòu)中。現(xiàn)在是時(shí)候以另一種方式支持?jǐn)?shù)據(jù)流:深層次的表單組件需要更新FilterableProductTable中的狀態(tài)。
React使這個(gè)數(shù)據(jù)流清晰易懂,以便理解你的程序是如何工作的,但是它需要比傳統(tǒng)的雙向數(shù)據(jù)綁定更多的輸入。
如果你嘗試在當(dāng)前版本的示例中鍵入或選中該框,則會(huì)看到React忽略了你的輸入。這是故意的,因?yàn)槲覀円呀?jīng)將輸入的值prop設(shè)置為始終等于從FilterableProductTable傳入的state。
讓我們想想我們想要發(fā)生的事情。我們希望確保每當(dāng)用戶更改表單時(shí),我們都會(huì)更新?tīng)顟B(tài)以反映用戶的輸入。由于組件應(yīng)該只更新自己的state,只要state需要更新時(shí),FilterableProductTable就會(huì)傳遞回調(diào)到SearchBar。我們可以使用輸入上的onChange事件來(lái)通知它。 FilterableProductTable傳遞的回調(diào)將調(diào)用setState(),并且應(yīng)用程序?qū)⒈桓隆?/p>
雖然這聽(tīng)起來(lái)很復(fù)雜,但實(shí)際上只是幾行代碼。你的數(shù)據(jù)如何在整個(gè)應(yīng)用程序中流動(dòng)變得非常明確。
就是這樣希望這篇文章可以讓你了解如何用React來(lái)構(gòu)建組件和應(yīng)用程序。雖然它可能比以前多一些代碼,但請(qǐng)記住,代碼的讀遠(yuǎn)遠(yuǎn)超過(guò)它的寫(xiě),并且讀取這個(gè)模塊化的顯式代碼非常容易。當(dāng)你開(kāi)始構(gòu)建大型組件庫(kù)時(shí),你將會(huì)體會(huì)到這種明確性和模塊性,并且通過(guò)代碼重用,你的代碼行將開(kāi)始縮小。
備注文中所有示例的HTML和CSS內(nèi)容如下:
body { padding: 5px }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/107352.html
摘要:函數(shù)式編程,一看這個(gè)詞,簡(jiǎn)直就是學(xué)院派的典范。所以這期周刊,我們就重點(diǎn)引入的函數(shù)式編程,淺入淺出,一窺函數(shù)式編程的思想,可能讓你對(duì)編程語(yǔ)言的理解更加融會(huì)貫通一些。但從根本上來(lái)說(shuō),函數(shù)式編程就是關(guān)于如使用通用的可復(fù)用函數(shù)進(jìn)行組合編程。 showImg(https://segmentfault.com/img/bVGQuc); 函數(shù)式編程(Functional Programming),一...
摘要:模板北京時(shí)間數(shù)據(jù)渲染數(shù)據(jù)渲染將數(shù)據(jù)和模板綁定在渲染聲明式渲染和普通模板不同的是,模板寫(xiě)在文件中,而不是的標(biāo)簽中。創(chuàng)建模板容器類北京時(shí)間渲染指令數(shù)據(jù)只用于存放可變的數(shù)據(jù)。北京時(shí)間通過(guò)算法計(jì)算如何更新視圖。 React 發(fā)展很快,概念也多,本文目的在于幫助初學(xué)者理清 React 核心概念。 React 及 React 生態(tài) showImg(http://www.ruanyifeng.com...
摘要:光憑一個(gè)是無(wú)法實(shí)現(xiàn)血緣關(guān)系疏遠(yuǎn)的組件之間的狀態(tài)同步的。就是為解決這個(gè)問(wèn)題而生的。,處理動(dòng)作的派發(fā),相當(dāng)于架構(gòu)的。我們的主角是,它也是目前社區(qū)最受歡迎的狀態(tài)管理框架。專題一覽考古實(shí)用中間件時(shí)間旅行 本文是『horseshoe·Redux專題』系列文章之一,后續(xù)會(huì)有更多專題推出來(lái)我的 GitHub repo 閱讀完整的專題文章來(lái)我的 個(gè)人博客 獲得無(wú)與倫比的閱讀體驗(yàn) React的橫空出世給...
摘要:沒(méi)有團(tuán)伙,單獨(dú)作案,干凈利落,便于封口。它最大的特點(diǎn)就是不可變。兄弟組件之間傳值原理和回調(diào)函數(shù)一樣,只不過(guò)這里父組件只是一個(gè)橋梁。父組件接收到回調(diào)函數(shù)的值以后,通過(guò)保存該值,并觸發(fā)另一個(gè)子組件重新渲染,重新渲染后另一個(gè)子組件便可以獲得該值。 本文是『horseshoe·React專題』系列文章之一,后續(xù)會(huì)有更多專題推出來(lái)我的 GitHub repo 閱讀完整的專題文章來(lái)我的 個(gè)人博客 ...
摘要:前端進(jìn)階進(jìn)階構(gòu)建項(xiàng)目一配置最佳實(shí)踐狀態(tài)管理之痛點(diǎn)分析與改良開(kāi)發(fā)中所謂狀態(tài)淺析從時(shí)間旅行的烏托邦,看狀態(tài)管理的設(shè)計(jì)誤區(qū)使用更好地處理數(shù)據(jù)愛(ài)彼迎房源詳情頁(yè)中的性能優(yōu)化從零開(kāi)始,在中構(gòu)建時(shí)間旅行式調(diào)試用輕松管理復(fù)雜狀態(tài)如何把業(yè)務(wù)邏輯這個(gè)故事講好和 前端進(jìn)階 webpack webpack進(jìn)階構(gòu)建項(xiàng)目(一) Webpack 4 配置最佳實(shí)踐 react Redux狀態(tài)管理之痛點(diǎn)、分析與...
閱讀 2408·2021-11-23 09:51
閱讀 1243·2021-11-22 13:52
閱讀 3675·2021-11-10 11:35
閱讀 1306·2021-10-25 09:47
閱讀 3078·2021-09-07 09:58
閱讀 1123·2019-08-30 15:54
閱讀 2887·2019-08-29 14:21
閱讀 3096·2019-08-29 12:20