?
This document uses PHP Chinese website manual Release
在我們看來,React 是用 JavaScript 構(gòu)建大型、快速 Web 應(yīng)用程序的主要方式。它在 Facebook 和 Instagram 上對我們的擴(kuò)展非常好。
React 的許多重要組成部分之一就是如何讓您在構(gòu)建應(yīng)用程序時(shí)考慮應(yīng)用程序。在本文中,我們將引導(dǎo)您使用 React 構(gòu)建可搜索產(chǎn)品數(shù)據(jù)表的思考過程。
想象一下,我們已經(jīng)有了一個(gè) JSON API 和來自我們設(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"}];
你要做的第一件事是在模擬中的每個(gè)組件(和子組件)周圍繪制框,并給它們所有名稱。如果您正在與設(shè)計(jì)師合作,他們可能已經(jīng)完成了這項(xiàng)工作,請與他們談?wù)?!他們?Photoshop 圖層名稱可能最終會(huì)成為您的React組件的名稱!
但你怎么知道自己的組件應(yīng)該是什么?只需使用相同的技巧來決定是否應(yīng)創(chuàng)建新的函數(shù)或?qū)ο?。一種這樣的技術(shù)是單一責(zé)任原則,也就是說,一個(gè)組件理想地只應(yīng)該做一件事。如果它最終增長,它應(yīng)該被分解成更小的子組件。
由于您經(jīng)常向用戶顯示 JSON 數(shù)據(jù)模型,因此您會(huì)發(fā)現(xiàn),如果您的模型構(gòu)建正確,那么您的 UI(以及您的組件結(jié)構(gòu))將很好地映射。這是因?yàn)閁I和數(shù)據(jù)模型傾向于遵循相同的信息架構(gòu),這意味著將 UI 分解為組件的工作通常是微不足道的。只需將其分解為完全代表您的數(shù)據(jù)模型的一部分的組件。
您會(huì)在這里看到我們的簡單應(yīng)用中有五個(gè)組件。我們已將每個(gè)組件代表的數(shù)據(jù)以斜體表示。
FilterableProductTable
(橙色):包含整個(gè)示例
SearchBar
(藍(lán)色):接收所有用戶輸入
ProductTable
(綠色):根據(jù)用戶輸入顯示和過濾數(shù)據(jù)收集
ProductCategoryRow
(綠松石):為每個(gè)類別顯示標(biāo)題
ProductRow
(紅色):顯示每個(gè)產(chǎn)品的一行
如果你看一下ProductTable
,你會(huì)發(fā)現(xiàn)表頭(包含“名稱”和“價(jià)格”標(biāo)簽)不是它自己的組件。這是一個(gè)偏好的問題,有兩種方法都有爭論。對于這個(gè)例子,我們將它作為其一部分,ProductTable
因?yàn)樗浅尸F(xiàn)數(shù)據(jù)收集的一部分,這是ProductTable
責(zé)任。然而,如果這個(gè)頭部變得復(fù)雜(即,如果我們要為分類添加可供性),將它作為它自己的ProductTableHeader
組件將是有意義的。
現(xiàn)在我們已經(jīng)確定了模擬中的組件,讓我們將它們安排到一個(gè)層次結(jié)構(gòu)中。這很容易。出現(xiàn)在模擬中另一個(gè)組件中的組件應(yīng)該在層次結(jié)構(gòu)中顯示為一個(gè)子組件:
FilterableProductTable
- `SearchBar`- `ProductTable`
- `ProductCategoryRow` - `ProductRow`
見筆思維陣營:第2步上CodePen。
現(xiàn)在您已經(jīng)擁有組件層次結(jié)構(gòu),現(xiàn)在可以實(shí)施您的應(yīng)用了。最簡單的方法是構(gòu)建一個(gè)接收數(shù)據(jù)模型并呈現(xiàn) UI 但不具有交互性的版本。最好解耦這些過程,因?yàn)闃?gòu)建一個(gè)靜態(tài)版本需要大量的輸入和思考,而添加交互性需要大量的思考,而不是大量的輸入。我們會(huì)看到為什么。
要構(gòu)建呈現(xiàn)數(shù)據(jù)模型的應(yīng)用程序的靜態(tài)版本,您需要構(gòu)建可重用其他組件并使用道具傳遞數(shù)據(jù)的組件。道具是一種將數(shù)據(jù)從父母傳遞給孩子的方式。如果您熟悉狀態(tài)的概念,請不要使用狀態(tài)來構(gòu)建此靜態(tài)版本。狀態(tài)僅用于交互性,即隨時(shí)間變化的數(shù)據(jù)。由于這是應(yīng)用程序的靜態(tài)版本,因此您不需要它。
您可以構(gòu)建自上而下或自下而上。也就是說,您可以從構(gòu)建層次結(jié)構(gòu)中較高級的組件(即以開始FilterableProductTable
)或以較低的組件(ProductRow
)開始構(gòu)建組件。在更簡單的例子中,自上而下通常會(huì)更容易,而在大型項(xiàng)目中,自下而上和編寫測試時(shí)更容易。
在這一步結(jié)束時(shí),您將擁有一個(gè)可重用組件的庫,用于呈現(xiàn)您的數(shù)據(jù)模型。組件只會(huì)有render()
方法,因?yàn)檫@是您的應(yīng)用程序的靜態(tài)版本。層次結(jié)構(gòu)頂部的組件(FilterableProductTable
)將把您的數(shù)據(jù)模型作為一個(gè)道具。如果您對基礎(chǔ)數(shù)據(jù)模型進(jìn)行了更改并ReactDOM.render()
再次調(diào)用,則UI將會(huì)更新。很容易看到你的UI如何更新以及在哪里進(jìn)行更改,因?yàn)闆]有任何復(fù)雜的事情發(fā)生。React 的單向數(shù)據(jù)流(也稱為單向綁定)可以保持一切模塊化和快速。
如果您需要幫助執(zhí)行此步驟,請參閱 React 文檔。
React 中有兩種“模型”數(shù)據(jù):道具和狀態(tài)。了解兩者之間的區(qū)別很重要; 如果您不確定區(qū)別是什么,請瀏覽官方的 React 文檔。
為了使您的 UI 交互,您需要能夠觸發(fā)對基礎(chǔ)數(shù)據(jù)模型的更改。React 使狀態(tài)變得簡單。
要正確構(gòu)建應(yīng)用程序,首先需要考慮應(yīng)用程序需要的最小可變狀態(tài)集。這里的關(guān)鍵是 DRY:不要重復(fù)自己。找出您的應(yīng)用程序需要的狀態(tài)的絕對最小表示形式,并計(jì)算您需要的其他所有內(nèi)容。例如,如果您正在創(chuàng)建一個(gè) TODO 列表,只需保留一組 TODO 項(xiàng)目; 不要為計(jì)數(shù)保留一個(gè)單獨(dú)的狀態(tài)變量。相反,當(dāng)您要渲染 TODO 計(jì)數(shù)時(shí),只需簡單計(jì)算 TODO 項(xiàng)目數(shù)組的長度。
考慮我們示例應(yīng)用程序中的所有數(shù)據(jù)。我們有:
產(chǎn)品的原始列表
用戶輸入的搜索文本
復(fù)選框的值
過濾的產(chǎn)品列表
我們來看看每一個(gè),并確定哪一個(gè)是狀態(tài)。只需詢問關(guān)于每一條數(shù)據(jù)的三個(gè)問題:
它是通過道具從父數(shù)據(jù)傳入嗎?如果是這樣,它可能不是陳述。
它隨著時(shí)間的推移而保持不變嗎 如果是這樣,它可能不是陳述。
你能基于組件中的任何其他狀態(tài)或道具來計(jì)算它嗎?如果是這樣,那不是陳述。
原來的產(chǎn)品清單是作為道具傳入的,所以這不是狀態(tài)。搜索文本和復(fù)選框似乎是狀態(tài)的,因?yàn)樗鼈冸S著時(shí)間而改變并且不能從任何東西計(jì)算。最后,過濾后的產(chǎn)品列表不是狀態(tài),因?yàn)樗梢酝ㄟ^將原始產(chǎn)品列表與復(fù)選框的搜索文本和值組合來計(jì)算。
最后,我們的狀態(tài)是:
用戶輸入的搜索文本
復(fù)選框的值
請參閱筆反思:在CodePen上的第4 步。
好的,我們已經(jīng)確定了最少的應(yīng)用程序狀態(tài)集。接下來,我們需要確定哪個(gè)組件變異或擁有這個(gè)狀態(tài)。
請記?。篟eact 是關(guān)于組件層次結(jié)構(gòu)下的單向數(shù)據(jù)流。它可能不會(huì)立即清楚哪個(gè)組件應(yīng)該擁有什么狀態(tài)。對于新手來說,這通常是最具挑戰(zhàn)性的部分,所以請按照以下步驟來弄清楚:
對于你的應(yīng)用程序中的每一個(gè)狀態(tài):
確定每個(gè)基于該狀態(tài)呈現(xiàn)某些內(nèi)容的組件。
找到一個(gè)共同的擁有者組件(一個(gè)單獨(dú)的組件超出了所有需要該層次中狀態(tài)的組件)。
無論是共同所有者,還是等級較高的其他組成部分都應(yīng)擁有該州。
如果你無法找到一個(gè)有意識(shí)擁有這個(gè)狀態(tài)的組件,可以創(chuàng)建一個(gè)新的組件,只是為了保存狀態(tài)并將它添加到公共所有者組件上方的層次結(jié)構(gòu)中的某處。
讓我們來看看我們的應(yīng)用程序的這個(gè)策略:
ProductTable
需要根據(jù)狀態(tài)過濾產(chǎn)品列表,同時(shí)SearchBar
需要顯示搜索文本和選中狀態(tài)。
共同的所有者組件是FilterableProductTable
。
從概念上講,過濾器文本和檢查值可以存在 FilterableProductTable
所以我們決定我們的國家居住在英國FilterableProductTable
。首先,添加一個(gè)實(shí)例屬性this.state = {filterText: '', inStockOnly: false}
到FilterableProductTable
's constructor
來反映你的應(yīng)用程序的初始狀態(tài)。然后,通過filterText
與inStockOnly
以ProductTable
和SearchBar
為道具。最后,使用這些道具過濾行中ProductTable
并設(shè)置表單域的值SearchBar
。
你可以開始看到你的應(yīng)用程序?qū)⑷绾伪憩F(xiàn):設(shè)置filterText
到"ball"
和刷新您的應(yīng)用程序。您會(huì)看到數(shù)據(jù)表已正確更新。
見筆思維陣營:第5步上CodePen。
到目前為止,我們已經(jīng)構(gòu)建了一個(gè)應(yīng)用程序,可以根據(jù)道具和狀態(tài)正確地呈現(xiàn)在層次結(jié)構(gòu)中?,F(xiàn)在是時(shí)候以另一種方式支持?jǐn)?shù)據(jù)流:深層次中的表單組件需要更新狀態(tài)FilterableProductTable
。
React 使這個(gè)數(shù)據(jù)流清晰易懂,以便理解你的程序如何工作,但它確實(shí)需要比傳統(tǒng)的雙向數(shù)據(jù)綁定更多的輸入。
如果您嘗試在當(dāng)前版本的示例中鍵入或選中該框,您會(huì)看到 React 忽略了您的輸入。這是故意的,因?yàn)槲覀円呀?jīng)設(shè)置value
的道具input
總是等于state
從傳遞FilterableProductTable
。
讓我們思考我們想要發(fā)生的事情。我們希望確保每當(dāng)用戶更改表單時(shí),我們都會(huì)更新狀態(tài)以反映用戶輸入。由于組件應(yīng)該只更新自己的狀態(tài),FilterableProductTable
因此SearchBar
只要狀態(tài)應(yīng)該更新,就會(huì)傳遞回調(diào)。我們可以使用onChange
輸入中的事件來通知它。通過的回調(diào)FilterableProductTable
將會(huì)調(diào)用setState()
,并且應(yīng)用程序?qū)⒈桓隆?/p>
雖然這聽起來很復(fù)雜,但它只是幾行代碼。這真的很明確你的數(shù)據(jù)如何在整個(gè)應(yīng)用程序中流動(dòng)。
希望這可以讓你了解如何使用 React 構(gòu)建組件和應(yīng)用程序。雖然它可能比您習(xí)慣的輸入要多一些,但請記住,代碼的讀取遠(yuǎn)遠(yuǎn)大于其編寫的代碼,并且讀取此模塊化的顯式代碼非常容易。當(dāng)你開始構(gòu)建大型組件庫時(shí),你會(huì)明白這種明確性和模塊性,并且隨著代碼的重用,你的代碼行將開始縮小。