摘要:搜索文本和多選框因?yàn)闀l(fā)生變化,且不能通過計(jì)算得出,所以是狀態(tài)。最后,過濾過的產(chǎn)品列表,可以通過原始產(chǎn)品列表搜索文本和多選框值計(jì)算出來,因此它不是狀態(tài)。從傳入的回調(diào)函數(shù)會調(diào)用,從而更新組件。
在使用JavaScript開發(fā)大型、快速的網(wǎng)頁應(yīng)用時(shí),React是我們的首選。在Facebook和Instagram,React很好地減少了我們的工作量。
React最強(qiáng)大部分之一,是讓你在開發(fā)應(yīng)用的同時(shí),按照開發(fā)應(yīng)用的思路去思考。這篇文章,將指導(dǎo)你通過幾個(gè)步驟,用React開發(fā)一個(gè)可搜索數(shù)據(jù)表
假設(shè)我們已經(jīng)有了一個(gè)JSON API和一個(gè)設(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分解為組件結(jié)構(gòu)
首先,在模型圖上框處每個(gè)組件(和子組件),并給組件命名。如果與設(shè)計(jì)師一起工作,他可能已經(jīng)心中有數(shù),不妨和他聊聊。他們Photoshop圖層名可能就能成為你的組件名!
但如何劃分父組件子組件呢?就像創(chuàng)建一個(gè)函數(shù)或?qū)ο笠粯印F渲兄皇菃我宦氊?zé)原則,理想情況下,一個(gè)組件僅做一件事。如果一個(gè)組件太大,它應(yīng)該被分解為更小的子組件。
如果經(jīng)常把JSON數(shù)據(jù)模型展現(xiàn)給用戶,你會發(fā)現(xiàn),若模型創(chuàng)建正確,UI(以及組件結(jié)構(gòu))能很好的反映數(shù)據(jù)。這是因?yàn)閁I和數(shù)據(jù)模型一定程度都是依附于信息架構(gòu)的,這意味著,將UI分解為組件并不是最重要的,最重要的是,被分解的組件能準(zhǔn)確表現(xiàn)你數(shù)據(jù)模型的一部分。
可以看出,我們的app有五個(gè)組件,我們用斜體表示每個(gè)組件所代表的數(shù)據(jù)。
FilterableProducTable(可過濾產(chǎn)品表,橙色):包含整個(gè)示例
SearchBar(搜索欄,藍(lán)色):接收用戶輸入
ProductTable(產(chǎn)品表,綠色):根據(jù)用戶輸入,展示和過濾數(shù)據(jù)集
ProductCategoryRow(產(chǎn)品分類行,藍(lán)綠色):顯示為每個(gè)分類的頭部
ProductRow(產(chǎn)品行,紅色):每個(gè)產(chǎn)品顯示為一行
觀察ProductTable,你會發(fā)現(xiàn)表頭(包含“Name”和“Price”標(biāo)簽部分)未被劃分為一個(gè)組件。表頭是否做為組件屬于個(gè)人偏好,至于使用哪種方式,以下可以作為參考。本例中,表頭作為ProductTable的一部分,因?yàn)楸眍^屬于渲染的數(shù)據(jù)集的一部分,所以表頭在ProductTable的職責(zé)范圍內(nèi)。但如果表頭變得更為復(fù)雜(如為添加排序功能),將表頭分離為ProductTableHeader更合理。
現(xiàn)在,我們已經(jīng)將模型劃分為了組件,接著要安排組件的層次結(jié)構(gòu)。這并不難,模型中一個(gè)組件出現(xiàn)在另一個(gè)組件的內(nèi)部,在層次結(jié)構(gòu)呈現(xiàn)為它的一個(gè)子級。
FilterableProductTable
SearchBar
ProductTable
ProductCategeoryRow
ProductRow
第二步:創(chuàng)建React靜態(tài)版本class ProductCategoryRow extends React.Component { render() { return; } } class ProductRow extends React.Component { render() { var name = this.props.product.stocked ? this.props.product.name : {this.props.product.name} ; return ( {this.props.category} ); } } class ProductTable extends React.Component { render() { var rows = []; var lastCategory = null; this.props.products.forEach(function(product) { if (product.category !== lastCategory) { rows.push( {name} {this.props.product.price} ); } rows.push( ); lastCategory = product.category; }); return (
Name | Price |
---|
現(xiàn)在,你已經(jīng)有了組件的層次結(jié)構(gòu),是時(shí)候?qū)崿F(xiàn)這個(gè)app了。最簡單的方式,利用數(shù)據(jù)模型渲染一個(gè)無交互的UI版本。創(chuàng)建組件靜態(tài)版本需要敲很多鍵盤,無需過多思考,而添加交互效果需要很多思考,不需要敲很多鍵盤,這正是極好的解耦過程。
用數(shù)據(jù)模型創(chuàng)建app的靜態(tài)版本,你在創(chuàng)建組件時(shí)會想著復(fù)用其它組件,想著用屬性來傳遞數(shù)據(jù),而屬性是父級向子級傳遞數(shù)據(jù)的方式。即便你熟悉狀態(tài)的概念,創(chuàng)建靜態(tài)版本時(shí)也不要用任何狀態(tài)。狀態(tài)僅用作交互,狀態(tài)數(shù)據(jù)隨時(shí)間而變化,因?yàn)殪o態(tài)版本無需任何交互,所以沒必要使用任何狀態(tài)。
在層級結(jié)構(gòu)中,既可以自上而下開始創(chuàng)建組件(從FilterableProductTable開始),也可以自下而上開始創(chuàng)建組件(從ProductRow開始)。簡單的例子中,自上而下的方式往往更簡單,對于大型項(xiàng)目來說,自下而上的方式更簡單,更容易測試。
該步完成時(shí),你已經(jīng)有了一些可復(fù)用的組件,這些組件渲染了你的數(shù)據(jù)模型。靜態(tài)版本的組件僅有render()方法。頂層組件(FilterableProductTable)會把數(shù)據(jù)模型作為一個(gè)屬性。如果數(shù)據(jù)模型發(fā)生變化,再次調(diào)用ReactDOM.render(),UI會被更新。你可以很容易的看出UI是如何被更新的,哪兒發(fā)生了改變。React的單向數(shù)據(jù)流(也叫單向數(shù)據(jù)綁定),使得一切變得模塊化,變得快速。
簡述:屬性與狀態(tài)屬性和狀態(tài)是React中的兩種數(shù)據(jù)模型,理解兩者的區(qū)別非常重要。如果你無法確定兩者的區(qū)別,請瀏覽官方文檔
第三步:確定描述UI的最少狀態(tài)為了讓UI可以交互,需要能夠觸發(fā)數(shù)據(jù)模型發(fā)生改變。React的狀態(tài)能讓此變得簡單。
為了能正確的創(chuàng)建app,你要考慮app需要的最少狀態(tài)。關(guān)鍵在于不重復(fù),使用最少的狀態(tài),計(jì)算出所有你需要的數(shù)據(jù)。例如,你創(chuàng)建一個(gè)TODO列表,只需要保留一個(gè)TODO項(xiàng)數(shù)組,不需要再使用一個(gè)變量來保存TODO項(xiàng)數(shù)量,當(dāng)你需要渲染TODO項(xiàng)數(shù)目時(shí),使用TODO項(xiàng)數(shù)組長度即可。
考慮示例應(yīng)用中所有的數(shù)據(jù),我們有:
原始產(chǎn)品列表
用戶輸入的搜索文本
多選框的值
過濾過的產(chǎn)品列表
簡單地問三個(gè)問題,讓我們過一遍,看看哪些是狀態(tài):
是否為從父組件傳入的屬性?如果是,或許不是狀態(tài)
是否是隨時(shí)間不變的?如果是,或許不是狀態(tài)
是否能通過組件中的其他狀態(tài)或?qū)傩杂?jì)算出來?如果是,不是狀態(tài)
原始產(chǎn)品列表是從屬性傳入的,因此不是狀態(tài)。搜索文本和多選框因?yàn)闀l(fā)生變化,且不能通過計(jì)算得出,所以是狀態(tài)。最后,過濾過的產(chǎn)品列表,可以通過原始產(chǎn)品列表、搜索文本和多選框值計(jì)算出來,因此它不是狀態(tài)。
最終,我們的狀態(tài)有:
用戶輸入的搜索文本
多選框的值
第四步:確定狀態(tài)應(yīng)該放置的位置class ProductCategoryRow extends React.Component { render() { return (); } } class ProductRow extends React.Component { render() { var name = this.props.product.stocked ? this.props.product.name : {this.props.product.name} ; return ( {this.props.category} ); } } class ProductTable extends React.Component { render() { var rows = []; var lastCategory = null; this.props.products.forEach((product) => { if (product.name.indexOf(this.props.filterText) === -1 || (!product.stocked && this.props.inStockOnly)) { return; } if (product.category !== lastCategory) { rows.push( {name} {this.props.product.price} ); } rows.push( ); lastCategory = product.category; }); return (
Name | Price |
---|
OK,我們已經(jīng)確定了app的最少狀態(tài)。接下來,我們要確定哪個(gè)組件是可變的,或者說狀態(tài)屬于哪個(gè)組件。
記住,React在層級及結(jié)構(gòu)中,數(shù)據(jù)向下流動(dòng)。這或許不能馬上清楚狀態(tài)究竟屬于哪個(gè)組件。這經(jīng)常也是初學(xué)者認(rèn)識React最有挑戰(zhàn)性的部分。所以,按照以下步驟,弄清這個(gè)問題:
對于應(yīng)用中的每個(gè)狀態(tài):
確認(rèn)每個(gè)需要這個(gè)狀態(tài)進(jìn)行渲染的組件
找到它們共有的父組件(層級結(jié)構(gòu)中,所有需要這個(gè)狀態(tài)的組件之上的組件)
讓共有父組件,或者比共有父組件更高層級的組件持有該狀態(tài)
如果你找不到合適的組件持有該狀態(tài),創(chuàng)建一個(gè)新的組件,簡單持有該狀態(tài)。把這個(gè)組件插入層級結(jié)構(gòu)共有父組件之上
將這個(gè)策略用到我們的應(yīng)用中:
ProductTable要根據(jù)狀態(tài)過濾產(chǎn)品列表,SearchBar要顯示搜索文本和多選框狀態(tài)
共有父組件是FilterableProductTable
FilterableProductTable持有過濾文本和多選框值很合理
Cool,就這樣決定把狀態(tài)放置到FilterableProductTable。首先,在FilterableProductTable的constructor中添加示例屬性this.state = {filterText: "", inStockOnly: false} ,來初始化狀態(tài)。然后,將filterText和isStockOnly作為屬性傳入ProductTable和SearchBar。最后,使用這些屬性過濾ProductTable中的行,和設(shè)置SearchBar中表單的值
第五步:添加反向數(shù)據(jù)流class ProductCategoryRow extends React.Component { render() { return (); } } class ProductRow extends React.Component { render() { var name = this.props.product.stocked ? this.props.product.name : {this.props.product.name} ; return ( {this.props.category} ); } } class ProductTable extends React.Component { render() { var rows = []; var lastCategory = null; this.props.products.forEach((product) => { if (product.name.indexOf(this.props.filterText) === -1 || (!product.stocked && this.props.inStockOnly)) { return; } if (product.category !== lastCategory) { rows.push( {name} {this.props.product.price} ); } rows.push( ); lastCategory = product.category; }); return (
Name | Price |
---|
目前為止,我們已經(jīng)用屬性和狀態(tài),創(chuàng)建了具有在層級結(jié)構(gòu)中至上而下的數(shù)據(jù)流?,F(xiàn)在,是時(shí)候支持反向的數(shù)據(jù)流動(dòng),讓底層表單組件能夠更新FilterableProductTable中的狀態(tài)。
React的單向數(shù)據(jù)流動(dòng)使得程序更加清晰易懂,但相比雙向數(shù)據(jù)綁定,確實(shí)需要多打些代碼。
如果你嘗試在當(dāng)前示例中,輸入文本或選擇多選框,會發(fā)現(xiàn)React忽略了你的輸入。這是故意的,因?yàn)槲覀円O(shè)置input的value屬性要恒等于FilterableProductTable傳入的狀態(tài)。
思考下我們期望的狀況。我們希望,表單中無論何時(shí)發(fā)生改變,用戶的輸入要能夠更新狀態(tài)。因?yàn)榻M件應(yīng)該只更新自己的狀態(tài),所以FilterableProductTable會將回調(diào)函數(shù)傳遞給SearchBar,SearchBar會在要更新狀態(tài)時(shí)調(diào)用回調(diào)函數(shù)。我們用onChange事件通知狀態(tài)更新。從FilterableProductTable傳入的回調(diào)函數(shù)會調(diào)用setState(),從而更新組件。
雖然聽起來有點(diǎn)復(fù)雜,但是也就添加幾行代碼。它非常清晰地表現(xiàn)出了app中的數(shù)據(jù)流動(dòng)。
就這樣希望此文能帶給你使用React創(chuàng)建組件和應(yīng)用的思路。盡管你可能需要打比平時(shí)更多的代碼,但記住,代碼看著總是遠(yuǎn)比打著的時(shí)候多,并且這些代碼都是模塊化、清晰而易讀的。當(dāng)你開始開發(fā)大型組件庫的時(shí)候,你將會慶幸React的數(shù)據(jù)流清晰和模塊化的特性,并且隨著代碼的復(fù)用,代碼規(guī)模會開始縮小。
原文鏈接thinking-in-react
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/81609.html
摘要:它并不是實(shí)際在內(nèi)部的工作方式,而且它只是一個(gè)提案,在未來都會有可能發(fā)生變化。這意味著,數(shù)據(jù)的存儲是獨(dú)立于組件之外的。因此,有一個(gè)訣竅就是你需要思考作為一組需要一個(gè)匹配一致的指針去管理的數(shù)組染陌譯。 原文地址:https://medium.com/@ryardley/... 譯文:染陌 (Github) 譯文地址:https://github.com/answershuto/Blog 轉(zhuǎn)...
摘要:注不做翻譯是中最小的構(gòu)建部件。在里渲染讓我們看一下在下面有在你文件中無處不在的標(biāo)簽我們會把這元素成為元素因?yàn)榈乃袞|西都會放在這個(gè)元素里面。通過方法,我們能吧渲染到我們根節(jié)點(diǎn)上。更新被渲染的是不可變的。 下面是react官方文檔的個(gè)人翻譯,如有翻譯錯(cuò)誤,請多多指出原文地址:https://facebook.github.io/re...特別感謝Hevaen,同時(shí)也向豪大React群所有...
摘要:一般來說,聲明式編程關(guān)注于發(fā)生了啥,而命令式則同時(shí)關(guān)注與咋發(fā)生的。聲明式編程可以較好地解決這個(gè)問題,剛才提到的比較麻煩的元素選擇這個(gè)動(dòng)作可以交托給框架或者庫區(qū)處理,這樣就能讓開發(fā)者專注于發(fā)生了啥,這里推薦一波與。 本文翻譯自FreeCodeCamp的from-zero-to-front-end-hero-part。 繼續(xù)譯者的廢話,這篇文章是前端攻略-從路人甲到英雄無敵的下半部分,在...
摘要:擁抱異步編程縱觀發(fā)展史也可以說成開發(fā)的發(fā)展史,你會發(fā)現(xiàn)異步徹底改變了這場游戲??梢赃@么說,異步編程已成為開發(fā)的根基。這也是你應(yīng)盡早在上投入大量時(shí)間的一處核心知識點(diǎn),這其中包含和等重要概念。這也是最突出的一項(xiàng)貢獻(xiàn)。 原文地址:Medium - Learning How to Learn JavaScript. 5 recommendations on how you should spend ...
閱讀 1412·2021-10-14 09:43
閱讀 4306·2021-09-27 13:57
閱讀 4596·2021-09-22 15:54
閱讀 2633·2021-09-22 10:54
閱讀 2433·2021-09-22 10:02
閱讀 2156·2021-08-27 13:11
閱讀 894·2019-08-29 18:44
閱讀 1673·2019-08-29 15:20