摘要:的單向數據流也稱為單向綁定使所有的事務更加模塊化也更加快速。第五步添加反向數據流到目前為止,我們已經構建了一個應用程序,可以根據和正確地呈現在層次結構中。傳遞的回調將調用,并且應用程序將被更新。
本文是對React官網《Thinking in React》一文的翻譯,通過這篇文章,React團隊向開發者們介紹了應該如果去構思一個web應用,為今后使用React進行web app的構建,打下基礎。 以下是正文。在我們團隊看來,React是使用JavaScript構建大型、快速的Web apps的首選方式。它已經在Facebook和Instagram項目中,表現出了非常好的可擴展性。
能夠按照構建的方式來思考web app的實現,是React眾多優點之一。在這篇文章中,我們將引導你進行使用React構建可搜索產品數據表的思考過程。
從設計稿開始想象一下,我們已經有了一個JSON API和來自設計師的設計稿。如下圖所示:
JSON API返回的數據如下所示:
[ {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分解為組件并分析層級結構
我們要做的第一件事就是給設計稿中的每個組件(和子組件)畫框,并給它們起名字。如果你正在和一個設計師合作,他可能已經幫你完成了這一步。他的Photoshop圖層名稱可能最終會成為你的React組件名稱!
但我們怎么知道自己的組件應該是什么?只需要使用一些通用的技巧來決定是否應該創建一個新的函數或對象。其中一個技巧叫做:單一責任原則。就是說,在理想情況下,一個組件應該只用來完成一件事。若非如此,則應該考慮將其分解成更小的子組件。
我們經常會向用戶展示JSON數據模型,那么你應該會發現,如果模型構建正確,那么你的UI(以及組件結構)應該能夠很好地映射數據模型。這是因為UI和數據模型傾向于遵循相同的信息架構,這意味著將UI分解為組件的工作通常是微不足道的?,F在我們把它分解成映射數據模型的組件如下:
現在我們的示例應用中有了五個組件,而且我們將每個組件代表的數據用斜體表示如下:
FilterableProductTable (橘黃色):包含整個示例的組件
SearchBar (藍色):接收所有的用戶輸入
ProductTable (綠色):根據用戶輸入顯示和過濾數據集
ProductCategoryRow (綠寶石色):顯示分類頭
ProductRow (紅色):每行顯示一條商品數據
細心的你會發現,在ProductTable中,表頭(包含名稱和價格標簽)不是一個組件。這是一個偏好的問題,有兩個方面的論點。在這個例子中,我們將其作為ProductTable組件的一部分,因為它是ProductTable負責渲染的數據集的一部分。但是,如果這個頭部變得很復雜(比如我們要支持排序),那么將其設置為ProductTableHeader這樣的組件肯定會更好一些。
現在我們已經確定了設計稿中的組件,下一步我們要給這些組件安排層次結構。這其實很容易:出現在一個組件中的組件應該在層次結構中顯示為一個子組件:
FilterableProductTable
SearchBar
ProductTable
ProductCategoryRow
ProductRow
第二步:用React構建一個靜態版本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 |
---|
現在我們已經有了組件層次結構,接下來可以實現應用程序了。最初的方案是構建一個使用數據模型渲染UI但不具有交互性的版本。最好將靜態版本和添加交互性進行解耦,因為構建一個靜態的版本需要大量的輸入卻不需要思考,而增加交互性需要大量的思考而不需要很多輸入。我們一會兒會知道為什么。
要構建渲染數據模型的靜態版本,需要構建可復用其他組件并使用props傳遞數據的組件。props是一種將數據從父組件傳遞給子組件的方式。如果你熟悉state的概念,請不要使用state來構建這個靜態版本。state只為實現交互性而保留,即隨時間變化的數據。由于這是應用程序的靜態版本,所以暫時不需要它。
你的構建過程可以自上而下或自下而上。也就是說,你可以從構建層次較高的組件(即FilterableProductTable)開始或較低的組件(ProductRow開始)。在簡單的例子中,自上而下通常比較容易,而在大型項目中,自下而上更容易而且更易于編寫測試用例。
在這一步的最后,你會有一個可重用組件的庫來渲染你的數據模型。這些組件只會有render()方法,因為這是你的應用程序的靜態版本。層次結構頂部的組件(FilterableProductTable)將把你的數據模型作為一個prop。如果你對基礎數據模型進行更改并再次調用ReactDOM.render(),則UI將會更新。這就很容易看到用戶界面是如何更新以及在哪里進行更改了,因為沒有任何復雜的事情發生。 React的單向數據流(也稱為單向綁定)使所有的事務更加模塊化也更加快速。
第三步:確定UI狀態的最小(但完整)表示形式為了使你的UI具有交互性,需要能夠觸發對基礎數據模型的更改。 React使用state讓這一切變得簡單。要正確構建應用程序,首先需要考慮應用程序需要的最小可變狀態集。這里的關鍵是:不要重復自己。找出應用程序需要的狀態的絕對最小表示,并計算需要的其他所有內容。例如,如果你正在創建一個TODO列表,只需要保存一個TODO項目的數組;不要為計數保留一個多帶帶的狀態變量。相反,當你要渲染TODO數量時,只需取TODO項目數組的長度即可。
考慮我們示例應用程序中的所有數據。我們有:
產品的原始列表
用戶輸入的搜索文本
復選框的值
過濾的產品列表
我們來看看每一個是哪一個state。這里有關于每條數據的三個問題:
是通過props從父組件傳入的嗎?如果是,那可能不是state。
它是否保持不變?如果是,那可能不是state。
你能基于組件中的任何其他state或props來計算它嗎?如果是,那不是state。
原來的產品清單是作為props傳入的,所以這不是state。搜索文本和復選框似乎是state,因為它們隨著時間而改變,不能從任何東西計算。最后,產品的過濾列表不是state,因為它可以通過將產品的原始列表與復選框的搜索文本和值組合來計算得到。
所以最后,我們的states是:
用戶輸入的搜索文本
復選框的值
第四步: 確定你的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 |
---|
現在我們已經確定了最小的一組應用程序state。接下來,我們需要確定哪個組件會改變或擁有這個state。
請記住:數據在React的組件層次結構中是單向流動的。它可能不清楚哪個組件應該擁有什么狀態。這通常是新手理解的最具挑戰性的部分,所以請按照以下步驟解決:
對于你的應用程序中的每一個state:
確定基于該state渲染某些內容的每個組件。
找到一個共同的擁有者組件(一個在所有需要該state的層次結構組件之上的組件)。
無論是共同所有者,還是高層次的其他組成部分,都應該擁有這個state。
如果你無法找到一個有意義的組件,那么只好創建一個新的組件來保存state,并將其添加到公共所有者組件上方的層次結構中的某個位置。
讓我們來看看我們的應用程序的這個策略:
ProductTable需要根據狀態過濾產品列表,而SearchBar需要顯示搜索文本和檢查狀態。
通用所有者組件是FilterableProductTable。
從概念上講,過濾器文本和選中的值存在于FilterableProductTable中是有意義的
酷,所以我們已經決定,我們的state存活在FilterableProductTable中。首先,將一個實例屬性this.state = {filterText:"",inStockOnly:false}添加到FilterableProductTable的構造函數中,以反映應用程序的初始狀態。然后,將filterText和inStockOnly作為prop傳遞給ProductTable和SearchBar。最后,使用這些props來篩選ProductTable中的行,并在SearchBar中設置表單域的值。
你可以看到你的應用程序的行為了:設置filterText為“ball”,并刷新你的應用程序。你將看到數據表已正確更新。
第五步:添加反向數據流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 |
---|
到目前為止,我們已經構建了一個應用程序,可以根據props和state正確地呈現在層次結構中。現在是時候以另一種方式支持數據流:深層次的表單組件需要更新FilterableProductTable中的狀態。
React使這個數據流清晰易懂,以便理解你的程序是如何工作的,但是它需要比傳統的雙向數據綁定更多的輸入。
如果你嘗試在當前版本的示例中鍵入或選中該框,則會看到React忽略了你的輸入。這是故意的,因為我們已經將輸入的值prop設置為始終等于從FilterableProductTable傳入的state。
讓我們想想我們想要發生的事情。我們希望確保每當用戶更改表單時,我們都會更新狀態以反映用戶的輸入。由于組件應該只更新自己的state,只要state需要更新時,FilterableProductTable就會傳遞回調到SearchBar。我們可以使用輸入上的onChange事件來通知它。 FilterableProductTable傳遞的回調將調用setState(),并且應用程序將被更新。
雖然這聽起來很復雜,但實際上只是幾行代碼。你的數據如何在整個應用程序中流動變得非常明確。
就是這樣希望這篇文章可以讓你了解如何用React來構建組件和應用程序。雖然它可能比以前多一些代碼,但請記住,代碼的讀遠遠超過它的寫,并且讀取這個模塊化的顯式代碼非常容易。當你開始構建大型組件庫時,你將會體會到這種明確性和模塊性,并且通過代碼重用,你的代碼行將開始縮小。
備注文中所有示例的HTML和CSS內容如下:
body { padding: 5px }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/107352.html
摘要:函數式編程,一看這個詞,簡直就是學院派的典范。所以這期周刊,我們就重點引入的函數式編程,淺入淺出,一窺函數式編程的思想,可能讓你對編程語言的理解更加融會貫通一些。但從根本上來說,函數式編程就是關于如使用通用的可復用函數進行組合編程。 showImg(https://segmentfault.com/img/bVGQuc); 函數式編程(Functional Programming),一...
摘要:模板北京時間數據渲染數據渲染將數據和模板綁定在渲染聲明式渲染和普通模板不同的是,模板寫在文件中,而不是的標簽中。創建模板容器類北京時間渲染指令數據只用于存放可變的數據。北京時間通過算法計算如何更新視圖。 React 發展很快,概念也多,本文目的在于幫助初學者理清 React 核心概念。 React 及 React 生態 showImg(http://www.ruanyifeng.com...
摘要:光憑一個是無法實現血緣關系疏遠的組件之間的狀態同步的。就是為解決這個問題而生的。,處理動作的派發,相當于架構的。我們的主角是,它也是目前社區最受歡迎的狀態管理框架。專題一覽考古實用中間件時間旅行 本文是『horseshoe·Redux專題』系列文章之一,后續會有更多專題推出來我的 GitHub repo 閱讀完整的專題文章來我的 個人博客 獲得無與倫比的閱讀體驗 React的橫空出世給...
摘要:沒有團伙,單獨作案,干凈利落,便于封口。它最大的特點就是不可變。兄弟組件之間傳值原理和回調函數一樣,只不過這里父組件只是一個橋梁。父組件接收到回調函數的值以后,通過保存該值,并觸發另一個子組件重新渲染,重新渲染后另一個子組件便可以獲得該值。 本文是『horseshoe·React專題』系列文章之一,后續會有更多專題推出來我的 GitHub repo 閱讀完整的專題文章來我的 個人博客 ...
閱讀 2341·2021-11-23 09:51
閱讀 1144·2021-11-22 13:52
閱讀 3617·2021-11-10 11:35
閱讀 1194·2021-10-25 09:47
閱讀 3000·2021-09-07 09:58
閱讀 1067·2019-08-30 15:54
閱讀 2823·2019-08-29 14:21
閱讀 3032·2019-08-29 12:20