摘要:支持回調函數,當內的表單輸入發生變化時可以即時通知其父組件。這是第一個問題,為了解決這個問題呢,需要對它的提兩個條件屬性上必須要接收一個回調函數,函數接收一個對象參數,參數結構如下的值為當前項產出的數據,可能是個對象也可能是字符串或者數值。
前言
最近在寫一個面向 React 初學者的系列教程玩轉 React,內容對有 React 開發經驗的同學來說可能太過于基礎和啰嗦,不太感興趣。所以我打算同時開始另外一個系列文章《React 開發實戰》。該系列主要面向有 React 開發經驗的同學,更側重 React 實戰,每一篇文章會跟大家一起開發一個 React 組件或者一個簡單有趣的 React 應用,這些組件或者應用往往滿足如下特點:
在我的實際項目中用到過的。
在常見的開源組件庫中沒有的。
有點小眾,但是在特定的業務場景下能很大地提高項目的開發效率。
可能還比較有趣。
如果這些組件能直接應用到大家的實際開發中去,那再好不過了;如果不能,能給大家一點啟發,我覺得這件事情也是很有價值的。
另外,每一篇文章后面都會附有本篇文章的完整示例和代碼。
問題描述大家應該都見過這種應用場景,頁面上的某一部分,需要能夠讓用戶添加任意多項。
可能是表單中的一個字段,如下所示。
也可能是表單的一部分,如下所示,用戶可以在一個表單內增加多個用戶信息,然后將用戶信息批量進行保存。
還有更{{BANNED}}的,如下所示,一個表單內用戶信息部分可以添加多份,每一個用戶信息中地址也可以添加多份。(Oh, My God. PM,你殺了我吧。)
還好,React 應付這種需求,還是小菜一碟。但是在一個 web 應用中有這么多的相似場景的話,如果我們挨個實現一遍,那真是太枯燥了,與搬磚無異。遇到這種情況,就需要我們把相同的功能抽象出來,做成組件,這將極大地提升你的開發效率。
基于這個場景,我們今天就開發一個能讓其 children 重復任意多份的組件,我們就稱之為 Repeat 吧。
你期望 Repeat 組件該怎么用在開發一個組件的時候,不要著急寫代碼,先想想你要把這個組件做成什么樣子,例如這個 Repeat 組件,我希望有如下特性:
Repeat 組件提供默認的,添加、移除按鈕。
點擊添加,將 React 的 children 復制一份,點擊移除將某一項移除。
當只有一項時不能移除。
Repeat 支持 onChange 回調函數,當 Repeat 內的表單輸入發生變化時可以即時通知其父組件。
然后在代碼中我期望可以這樣來用 Repeat 這個組件:
class App extends React.Component { handleChange(items) { console.info(items); } render() {this.handleChange(items)}> } }
OK,就是這么簡單,這樣 Input 組件就可以重復加添多份了。基于這個構想,我們來實現 Repeat 這個組件。
開始實現 Repeat 組件class Repeat extends React.Component { constructor(props) { super(props); this.state = { items: [""], }; } handleChange(e, index) { const items = [...this.state.items]; items[index] = e.target.value; this.setState({ items }); this.props.onChange(items); } handleAddItem(e, index) { e.preventDefault(); const items = [...this.state.items]; items.splice(index, 0, ""); this.setState({ items }); } handleRemoveItem(e, index) { e.preventDefault(); if (this.state.items.length === 1) return; const items = [...this.state.items]; items.splice(index, 1); this.setState({ items }); } render() { const children = React.Children.only(this.props.children); const elementItems = this.state.items.map((item, index) => ({ React.cloneElement(children, { onChange: e => this.handleChange(e, index), value: item, }) })); return{elementItems}; } }
代碼很簡單,簡單解釋一下:
組件的 state 中持有 items 字段來保存每一個項的數據。
render 時先獲取到唯一的 children,然后 map 組件 state 中的 items,將每一項映射為 children 的一個副本。并為這個副本傳入兩個屬性,onChange 接收每一項的數據變化,value 傳遞每一項當前應展示的值。
另外 Repeat 為每一項準備了一個“添加”按鈕和一個“移除”按鈕,用來在當前項位置新增一項或者移除當前項。原理就是將 this.state.items 中對應下標處的數組元素刪掉就好了。
到此,Repeat 是不是大致有模有樣了呢。需要提醒大家的是,React.cloneElement 和 React.Children.xxx 這些 api 通常只會在這種公共組件中使用,在大部分場景,盡量少用。
跟 children 有個約定有些同學可能已經發現了,上面例子中, Repeat 的 children 是個 input,那如果是一個其他的組件不就完蛋了嘛。
這是第一個問題,為了解決這個問題呢,Repeat 需要對它的 children 提兩個條件:
屬性上必須要接收一個 onChange 回調函數,函數接收一個對象參數,參數結構如下:
{ target: { value: "xxxx" } }
value 的值為當前項產出的數據,可能是個對象也可能是字符串或者數值。沒錯,我就是為了兼容 input event 的數據結構。你當然可以用任何你喜歡的且方便處理的數據結構。
children 組件需要接收一個 value 屬性,以展示其擁有的值。也就是說 children 組件應當是一個受控的(controlled)組件。
這就是一個協議,你希望某個組件內通過 Repeat 組件方便地添加多份并能獲取到一組數據,那就必須要遵守這個協議。有同學可能會說為什么不搞的智能一點呢?嗯,這里我想分享一點個人經驗:有些時候,尤其是在業務開發過程中,把公共部分抽取出來復用即可,點到為止,沒有必要搞得那么“強大”,剩下的事情讓一個很容易遵守的協議來完成,其實效率會更高,更容易讓人理解。
其實在計算機的世界中處處充滿了協議,例如你想讓 HTTP Server 返回正確的響應,你必須要遵循 http 協議來和它通信;你生產的顯卡能買的出去,必須要遵守相應的協議,要能插到別人家生產的主板上。
扯遠了!收!
對,有了上面這個約定以后,Repeat 一行代碼未加,是不是感覺功能完善了許多?嗯,就是這個目的。現在我們來實現一下文章開始時候說的第二個場景。
聰明的你一定已經知道該怎么做了,沒錯,只要我們實現一個 UserForm 組件,并讓他滿足上面的約定即可。請看代碼:
class UserForm extends React.Component { handleFieldChange(e) { const { name, value } = e.target; const formData = { ...this.props.value, [name]: value, } this.props.onChange({ target: { value: formData, } }); } render() { const formData = this.props.value || {}; return () } }this.handleFieldChange(e)} />this.handleFieldChange(e)} />
為了讓代碼更簡潔,我把 UserForm 這個組件實現為了一個支持受控的組件,但是在目前的業務場景下已經足夠了,在實際情況下,你可以按需調整。
通過這個例子,還希望大家能體會到組件拆分的一個好處。就是,UserForm 和 Repeat 拆分成兩個組件以后,UserForm 的復用性會更強。可以想象一下,當用戶被批量添加以后,是不是有可能在編輯單個用戶的時候,可以繼續使用這個組件。
好啦,關于第三個場景我想就沒有必要再實現一遍了,Repeat 嵌套多少層其實都是可以的。
更進一步實際上在實際應用中,Repeat 這個組件還需要做進一步完善,其中一個就是樣式,還有可能在不同的場景下,雖然交互都是這樣,但樣式會有所差異。另外默認是“添加”、“移除”兩個文字按鈕,說不定實際業務場景中是兩個 +,- 的圖標按鈕;還有可能“添加”、“移除”的位置為有所變化。
這些問題怎么處理呢?下面給大家描述下思路,具體代碼就不寫了,如果有什么疑問可以給我留言。
關于樣式,你可以給 Repeat 添加 itemClassName 和 buttonsClassName 兩個屬性分別為每一項和按鈕區域的 css class。這樣你就可以在不同的場景下指定不同的樣式了。
關于如何將文字按鈕改為圖標按鈕,你可以給 Repeat 添加 renderButtons 這樣一個函數屬性,如果未指定則用默認的方式渲染按鈕,如果有則勇氣返回值渲染屬性。
最后這是本篇文章的代碼:https://codepen.io/Sarike/pen...
好啦,文章就到這吧,如果有什么疑問可以給我留言。謝謝大家,祝大家國慶、中秋節快樂。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/88797.html
摘要:支持回調函數,當內的表單輸入發生變化時可以即時通知其父組件。這是第一個問題,為了解決這個問題呢,需要對它的提兩個條件屬性上必須要接收一個回調函數,函數接收一個對象參數,參數結構如下的值為當前項產出的數據,可能是個對象也可能是字符串或者數值。 前言 最近在寫一個面向 React 初學者的系列教程玩轉 React,內容對有 React 開發經驗的同學來說可能太過于基礎和啰嗦,不太感興趣。所...
摘要:前端日報精選劉海打理指北中的錯誤處理模式與反模式譯圖解和譯你并不知道中文裝飾器讓你的代碼更簡潔眾成翻譯第期每個程序員第一份工作前應該知道的件事中的不變性眾成翻譯寫的一次小結掘金內部機制探秘和文末附彩蛋和源碼前端雜談開發實戰 2017-09-30 前端日報 精選 iPhone X 劉海打理指北React16中的錯誤處理ES6 Promise:模式與反模式「譯」圖解 ArrayBuffer...
摘要:組件將元素作為結果返回。是把數據從項目傳到的有效載荷。有以下職責維持應用的提供方法獲取提供方法更新通過注冊監聽器通過返回的函數注銷監聽器。系列目錄前端大統一時代即將來臨項目實戰環境搭建項目實戰基本原理項目實戰登錄頁面編輯中 React相關 React 是一個采用聲明式,高效而且靈活的用來構建用戶界面的框架。 JSX 本質上來講,JSX 只是為React.createElement(co...
閱讀 3636·2021-11-23 09:51
閱讀 1984·2021-11-16 11:42
閱讀 3208·2021-11-08 13:20
閱讀 1094·2019-08-30 15:55
閱讀 2200·2019-08-30 10:59
閱讀 1231·2019-08-29 14:04
閱讀 1009·2019-08-29 12:41
閱讀 1982·2019-08-26 12:22