本文涵蓋如下受控組件:css
同時也包含:html
在學習 React.js 時我遇到了一個問題,那就是很難找到受控組件的真實示例。受控文本輸入框的例子卻是很豐富,但複選框、單選框、下拉選擇框的例子卻不盡人意。node
本文列舉了真實的受控表單組件示例,要是我在學習 React 的時候早點發現這些示例就行了。除了日期和時間輸入框須要另開篇幅詳細討論,文中列舉了全部的表單元素。react
有時候,爲了減小開發時間,有時候人們很容易爲了一些東西(譬如表單元素)引入一個庫。而對於表單,我發現當須要添加自定義行爲或表單校驗時,使用庫會讓事情變得更復雜。不過一旦掌握合適的 React 模式,你會發現構建表單組件並不是難事,而且有些東西徹底能夠本身動手,豐衣足食。請把本文的示例代碼看成你建立表單組件的起點或靈感之源。ios
除了提供單獨的組件代碼,我還將這些組件放進表單中,方便你理解子組件如何更新父組件 state ,以及接下來父組件如何經過 props(單向數據流)更新子組件。git
注意:本表單示例由很讚的 create-react-app 構建配置生成,若是你尚未安裝該構建配置,我強烈推薦你安裝一下(npm install -g create-react-app
)。目前這是搭建 React 應用最簡單的方式。github
受控組件有兩個特色:npm
onChange
事件發生時控制它們的數據,而不是一次性地獲取表單數據(例如用戶點提交按鈕時)。「被控制「 的表單數據保存在 state 中(在本文示例中,是父組件或容器組件的 state)。這個單向循環 —— (數據)從(1)子組件輸入到(2)父組件的 state,接着(3)經過 props 回到子組件,就是 React.js 應用架構中單向數據流的含義。json
咱們的頂級組件叫作 App
,這是它的代碼:
import React, { Component } from 'react'; import '../node_modules/spectre.css/dist/spectre.min.css'; import './styles.css'; import FormContainer from './containers/FormContainer'; class App extends Component { render() { return ( <div className="container"> <div className="columns"> <div className="col-md-9 centered"> <h3>React.js Controlled Form Components</h3> <FormContainer /> </div> </div> </div> ); } } export default App;
App
只負責渲染 index.html
頁面。整個 App
組件最有趣的部分是 13 行,FormContainer
組件。
是時候說起一下容器(智能)組件和普通(木偶)組件了。容器組件包含業務邏輯,它會發起數據請求或進行其餘業務操做。普通組件則從它的父(容器)組件接收數據。木偶組件有可能觸發更新 state (譯註:容器組件的 state)這類邏輯行爲,但它僅經過從父(容器)組件傳入的方法來達到該目的。
注意: 雖然在咱們的表單應用裏父組件就是容器組件,但我要強調,並不是全部的父組件都是容器組件。木偶組件嵌套木偶組件也是能夠的。
FormContainer
組件包含了表單元素組件,它在生命週期鉤子方法 componentDidMount
裏請求數據,此外還包含更新表單應用 state 的邏輯行爲。在下面的預覽代碼裏,我移除了表單元素的 props 和 change 事件處理方法,這樣看起來更簡潔清晰(拉到文章底部,能夠看到完整代碼)。
import React, {Component} from 'react'; import CheckboxOrRadioGroup from '../components/CheckboxOrRadioGroup'; import SingleInput from '../components/SingleInput'; import TextArea from '../components/TextArea'; import Select from '../components/Select'; class FormContainer extends Component { constructor(props) { super(props); this.handleFormSubmit = this.handleFormSubmit.bind(this); this.handleClearForm = this.handleClearForm.bind(this); } componentDidMount() { fetch('./fake_db.json') .then(res => res.json()) .then(data => { this.setState({ ownerName: data.ownerName, petSelections: data.petSelections, selectedPets: data.selectedPets, ageOptions: data.ageOptions, ownerAgeRangeSelection: data.ownerAgeRangeSelection, siblingOptions: data.siblingOptions, siblingSelection: data.siblingSelection, currentPetCount: data.currentPetCount, description: data.description }); }); } handleFormSubmit() { // 提交邏輯寫在這 } handleClearForm() { // 清除表單邏輯寫在這 } render() { return ( <form className="container" onSubmit={this.handleFormSubmit}> <h5>Pet Adoption Form</h5> <SingleInput /> {/* Full name text input */} <Select /> {/* Owner age range select */} <CheckboxOrRadioGroup /> {/* Pet type checkboxes */} <CheckboxOrRadioGroup /> {/* Will you adopt siblings? radios */} <SingleInput /> {/* Number of current pets number input */} <TextArea /> {/* Descriptions of current pets textarea */} <input type="submit" className="btn btn-primary float-right" value="Submit"/> <button className="btn btn-link float-left" onClick={this.handleClearForm}>Clear form</button> </form> ); }
咱們勾勒出了應用基礎結構,接下來咱們一塊兒瀏覽下每一個子組件的細節。
<SingleInput />
組件該組件能夠是 text
或 number
輸入框,這取決於傳入的 props。經過 React 的 PropTypes,咱們能夠很是好地記錄組件拿到的 props。若是漏傳 props 或傳入錯誤的數據類型, 瀏覽器的控制檯中會出現警告信息。
下面列舉 <SingleInput />
組件的 PropTypes:
SingleInput.propTypes = { inputType: React.PropTypes.oneOf(['text', 'number']).isRequired, title: React.PropTypes.string.isRequired, name: React.PropTypes.string.isRequired, controlFunc: React.PropTypes.func.isRequired, content: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.number, ]).isRequired, placeholder: React.PropTypes.string, };
PropTypes 聲明瞭 prop 的類型(string、 number、 array、 object 等等),其中包括了必需(isRequired
)和非必需的 prop,固然它還有更多的用途(欲知更多細節,請查看 React 文檔)。
下面咱們逐個討論這些 PropType:
inputType
:接收兩個字符串:'text'
或 'number'
。該設置指定渲染 <input type="text" />
組件或 <input type="number" />
組件。title
:接收一個字符串,咱們將它渲染到輸入框的 label 元素中。name
:輸入框的 name 屬性。controlFunc
:它是從父組件或容器組件傳下來的方法。由於該方法掛載在 React 的 onChange 處理方法上,因此每當輸入框的輸入值改變時,該方法都會被執行,從而更新父組件或容器組件的 state。content
:輸入框內容。受控輸入框只會顯示經過 props 傳入的數據。placeholder
:輸入框的佔位符文本,是一個字符串。既然該組件不須要任何邏輯行爲和內部 state,那咱們能夠將它寫成純函數組件(pure functional component)。咱們將純函數組件賦值給一個 const
常量上。下面是 <SingleInput />
組件的全部代碼。本文列舉的全部表單元素組件都是純函數組件。
import React from 'react'; const SingleInput = (props) => ( <div className="form-group"> <label className="form-label">{props.title}</label> <input className="form-input" name={props.name} type={props.inputType} value={props.content} onChange={props.controlFunc} placeholder={props.placeholder} /> </div> ); SingleInput.propTypes = { inputType: React.PropTypes.oneOf(['text', 'number']).isRequired, title: React.PropTypes.string.isRequired, name: React.PropTypes.string.isRequired, controlFunc: React.PropTypes.func.isRequired, content: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.number, ]).isRequired, placeholder: React.PropTypes.string, }; export default SingleInput;
接着,咱們用 handleFullNameChange
方法(它被傳入到 controlFunc
prop 屬性)來更新 <FormContainer />
容器組件的 state。
// FormContainer.js handleFullNameChange(e) { this.setState({ ownerName: e.target.value }); } // constructor 方法裏別漏掉了這行: // this.handleFullNameChange = this.handleFullNameChange.bind(this);
隨後咱們將容器組件更新後的 state (譯註:這裏指 state 上掛載的 ownerName 屬性)經過 content
prop 傳回 <SingleInput />
組件。
<Select />
組件選擇組件(就是下拉選擇組件),接收如下 props:
Select.propTypes = { name: React.PropTypes.string.isRequired, options: React.PropTypes.array.isRequired, selectedOption: React.PropTypes.string, controlFunc: React.PropTypes.func.isRequired, placeholder: React.PropTypes.string };
name
:填充表單元素上 name
屬性的字符串變量。options
:是一個數組(本例是字符串數組)。經過在組件的 render 方法中使用 props.options.map()
, 該數組中的每一項都會被渲染成一個選擇項。selectedOption
:用以顯示錶單填充的默認選項,或用戶已選擇的選項(例如當用戶編輯以前已提交過的表單數據時,可使用這個 prop)。controlFunc
:它是從父組件或容器組件傳下來的方法。由於該方法掛載在 React 的 onChange 處理方法上,因此每當改變選擇框組件的值時,該方法都會被執行,從而更新父組件或容器組件的 state。placeholder
:做爲佔位文本的字符串,用來填充第一個 <option>
標籤。本組件中,咱們將第一個選項的值設置成空字符串(參看下面代碼的第 10 行)。import React from 'react'; const Select = (props) => ( <div className="form-group"> <select name={props.name} value={props.selectedOption} onChange={props.controlFunc} className="form-select"> <option value="">{props.placeholder}</option> {props.options.map(opt => { return ( <option key={opt} value={opt}>{opt}</option> ); })} </select> </div> ); Select.propTypes = { name: React.PropTypes.string.isRequired, options: React.PropTypes.array.isRequired, selectedOption: React.PropTypes.string, controlFunc: React.PropTypes.func.isRequired, placeholder: React.PropTypes.string }; export default Select;
請注意 option 標籤中的 key
屬性(第 14 行)。React 要求被重複操做渲染的每一個元素必須擁有獨一無二的 key
值,咱們這裏的 .map()
方法就是所謂的重複操做。既然選擇項數組中的每一個元素是獨有的,咱們就把它們當成 key
prop。該 key
值協助 React 追蹤 DOM 變化。雖然在循環操做或 mapping 時忘加 key
屬性不會中斷應用,可是瀏覽器的控制檯裏會出現警告,而且渲染性能將受到影響。
如下是控制選擇框組件(記住,該組件存在於 <FormContainer />
組件中)的處理方法(該方法從 <FormContainer />
組件傳入到子組件的 controlFun
prop 中)
// FormContainer.js handleAgeRangeSelect(e) { this.setState({ ownerAgeRangeSelection: e.target.value }); } // constructor 方法裏別漏掉了這行: // this.handleAgeRangeSelect = this.handleAgeRangeSelect.bind(this);
<CheckboxOrRadioGroup />
組件<CheckboxOrRadioGroup />
不同凡響, 它從 props 拿到傳入的數組(像此前 <Select />
組件的選項數組同樣),經過遍歷數組來渲染一組表單元素的集合 —— 能夠是複選框集合或單選框集合。
讓咱們深刻 PropTypes 來更好地理解 <CheckboxOrRadioGroup />
組件。
CheckboxGroup.propTypes = { title: React.PropTypes.string.isRequired, type: React.PropTypes.oneOf(['checkbox', 'radio']).isRequired, setName: React.PropTypes.string.isRequired, options: React.PropTypes.array.isRequired, selectedOptions: React.PropTypes.array, controlFunc: React.PropTypes.func.isRequired };
title
:一個字符串,用以填充單選或複選框集合的 label 標籤內容。type
:接收 'checkbox'
或 'radio'
兩種配置的一種,並用指定的配置渲染輸入框(譯註:這裏指複選輸入框或單選輸入框)。setName
:一個字符串,用以填充每一個單選或複選框的 name
屬性值。options
:一個由字符串元素組成的數組,數組元素用以渲染每一個單選框或複選框的值和 label 的內容。例如,['dog', 'cat', 'pony']
數組中的元素將會渲染三個單選框或複選框。selectedOptions
:一個由字符串元素組成的數組,用來表示預選項。在示例 4 中,若是 selectedOptions
數組包含 'dog'
和 'pony'
元素,那麼相應的兩個選項會被渲染成選中狀態,而 'cat'
選項則被渲染成未選中狀態。當用戶提交表單時,該數組將會是用戶的選擇數據。controlFunc
:一個方法,用來處理從 selectedOptions
數組 prop 中添加或刪除字符串的操做。這是本表單應用中最有趣的組件,讓咱們來看一下:
import React from 'react'; const CheckboxOrRadioGroup = (props) => ( <div> <label className="form-label">{props.title}</label> <div className="checkbox-group"> {props.options.map(opt => { return ( <label key={opt} className="form-label capitalize"> <input className="form-checkbox" name={props.setName} onChange={props.controlFunc} value={opt} checked={ props.selectedOptions.indexOf(opt) > -1 } type={props.type} /> {opt} </label> ); })} </div> </div> ); CheckboxOrRadioGroup.propTypes = { title: React.PropTypes.string.isRequired, type: React.PropTypes.oneOf(['checkbox', 'radio']).isRequired, setName: React.PropTypes.string.isRequired, options: React.PropTypes.array.isRequired, selectedOptions: React.PropTypes.array, controlFunc: React.PropTypes.func.isRequired }; export default CheckboxOrRadioGroup;
checked={ props.selectedOptions.indexOf(option) > -1 }
這一行代碼表示單選框或複選框是否被選中的邏輯。
屬性 checked
接收一個布爾值,用來表示 input 組件是否應該被渲染成選中狀態。咱們在檢查到 input 的值是不是 props.selectedOptions
數組的元素之一時生成該布爾值。myArray.indexOf(item)
方法返回 item 在數組中的索引值。若是 item 不在數組中,返回 -1
,所以,咱們寫了 > -1
。
注意,0
是一個合法的索引值,因此咱們須要 > -1
,不然代碼會有 bug。若是沒有 > -1
,selectedOptions
數組中的第一個 item —— 其索引爲 0 —— 將永遠不會被渲染成選中狀態,由於 0
是一個類 false
的值(譯註:在 checked
屬性中,0
會被當成 false
處理)。
本組件的處理方法一樣比其餘的有趣。
handlePetSelection(e) { const newSelection = e.target.value; let newSelectionArray; if(this.state.selectedPets.indexOf(newSelection) > -1) { newSelectionArray = this.state.selectedPets.filter(s => s !== newSelection) } else { newSelectionArray = [...this.state.selectedPets, newSelection]; } this.setState({ selectedPets: newSelectionArray }); }
如同全部處理方法同樣,事件對象被傳入方法,這樣一來咱們就能拿到事件對象的值(譯註:準確來講,應該是事件目標元素的值)。咱們將該值賦給newSelection
常量。接着咱們在函數頂部附近定義 newSelectionArray
變量。由於咱們將在一個 if/else
代碼塊裏對該變量進行賦值,因此用 let
而非 const
來定義它。咱們在代碼塊外部進行定義,這樣一來被定義變量的做用域就是函數內部的最外沿,而且函數內的代碼塊都能訪問到外部定義的變量。
該方法須要處理兩種可能的狀況。
若是 input 組件的值不在 selectedOptions
數組中,咱們要將值添加進該數組。
若是 input 組件的值在 selectedOptions
數組中,咱們要從數組中刪除該值。
添加(第 8 - 10 行):
爲了將新值添加進選項數組,咱們經過解構舊數組(數組前的三點...
表示解構)建立一個新數組,而且將新值添加到數組的尾部 newSelectionArray = [...this.state.selectedPets, newSelection];
。
注意,咱們建立了一個新數組,而不是經過相似 .push()
的方法來改變原數組。不改變已存在的對象和數組,而是建立新的對象和數組,這在 React 中是又一個最佳實踐。開發者這樣作能夠更容易地跟蹤 state 的變化,而第三方 state 管理庫,如 Redux 則能夠作高性能的淺比較,而不是阻塞性能的深比較。
刪除(第 6 - 8 行):if
代碼塊藉助此前用到的 .indexOf()
小技巧,檢查選項是否在數組中。若是選項已經在數組中,經過.filter()
方法,該選項將被移除。 該方法返回一個包含全部知足 filter 條件的元素的新數組(記住要避免在 React 直接修改數組或對象!)。
newSelectionArray = this.state.selectedPets.filter(s => s !== newSelection)
在這種狀況下,除了傳入到方法中的選項以外,其餘選項都會被返回。
<TextArea />
組件<TextArea />
和咱們已提到的那些組件很是類似,除了 resize
和 rows
,目前你應該對它的 props 很熟悉了。
TextArea.propTypes = { title: React.PropTypes.string.isRequired, rows: React.PropTypes.number.isRequired, name: React.PropTypes.string.isRequired, content: React.PropTypes.string.isRequired, resize: React.PropTypes.bool, placeholder: React.PropTypes.string, controlFunc: React.PropTypes.func.isRequired };
title
:接收一個字符串,用以渲染文本域的 label 標籤內容。rows
:接收一個整數,用來指定文本域的行數。name
:文本域的 name 屬性。content
:文本域的內容。受控組件只會顯示經過 props 傳入的數據。resize
: 接受一個布爾值,用來指定文本域可否調整大小。placeholder
:充當文本域佔位文本的字符串。controlFunc
: 它是從父組件或容器組件傳下來的方法。由於該方法掛載在 React 的 onChange 處理方法上,因此每當改變選擇框組件的值時,該方法都會被執行,從而更新父組件或容器組件的 state。<TextArea />
組件的完整代碼:
import React from 'react'; const TextArea = (props) => ( <div className="form-group"> <label className="form-label">{props.title}</label> <textarea className="form-input" style={props.resize ? null : {resize: 'none'}} name={props.name} rows={props.rows} value={props.content} onChange={props.controlFunc} placeholder={props.placeholder} /> </div> ); TextArea.propTypes = { title: React.PropTypes.string.isRequired, rows: React.PropTypes.number.isRequired, name: React.PropTypes.string.isRequired, content: React.PropTypes.string.isRequired, resize: React.PropTypes.bool, placeholder: React.PropTypes.string, controlFunc: React.PropTypes.func.isRequired }; export default TextArea;
<TextAreas />
組件的控制方法和 <SingleInput />
一模一樣。細節部分請參考 <SingleInput />
組件。
handleClearForm
和 handleFormSubmit
方法操做整個表單。
既然咱們在表單的各處都使用了單向數據流,那麼清除表單數據對咱們來講也是小菜一碟。<FormContainer />
組件的 state 控制了每一個表單元素的值。該容器的 state 經過 props 傳入子組件。只有當 <FormContainer />
組件的 state 改變時,表單組件顯示的值纔會改變。
清除表單子組件中顯示的數據很簡單,只要把容器的 state (譯註:這裏是指 state 對象上掛載的各個變量)設置成空數組和空字符串就能夠了(若是有數字輸入框的話則是將值設置成 0
)。
handleClearForm(e) { e.preventDefault(); this.setState({ ownerName: '', selectedPets: [], ownerAgeRangeSelection: '', siblingSelection: [], currentPetCount: 0, description: '' }); }
注意,e.preventDefault()
阻止了頁面從新加載,接着 setState()
方法用來清除表單數據。
爲了提交表單數據,咱們從 state 中抽取須要提交的屬性值,建立了一個對象。接着使用 AJAX 庫或技術將這些數據發送給 API(本文不包含此類內容)。
handleFormSubmit(e) { e.preventDefault(); const formPayload = { ownerName: this.state.ownerName, selectedPets: this.state.selectedPets, ownerAgeRangeSelection: this.state.ownerAgeRangeSelection, siblingSelection: this.state.siblingSelection, currentPetCount: this.state.currentPetCount, description: this.state.description }; console.log('Send this in a POST request:', formPayload); this.handleClearForm(e); }
請注意咱們在提交數據後執行 this.handleClearForm(e)
清除了表單。
受控表單組件很是適合自定義表單校驗。假設要從 <TextArea />
組件中排除字母 "e",能夠這樣作:
handleDescriptionChange(e) { const textArray = e.target.value.split('').filter(x => x !== 'e'); console.log('string split into array of letters',textArray); const filteredText = textArray.join(''); this.setState({ description: filteredText }); }
把 e.target.value
字符串分割成字母數組,就生成了上述的 textArray
。這樣字母 「e」 (或其餘設法排除的字母)就被過濾掉了。再把剩餘的字母組成的數組拼成字符串,最後用該新字符串去設置組件 state。還不錯吧?
以上代碼放在本文的倉庫中,但我將它們註釋掉了,你能夠按本身的需求自由地調整。
<FormContainer />
組件下面是我承諾給大家的 <FormContainer />
組件完整代碼,
import React, {Component} from 'react'; import CheckboxOrRadioGroup from '../components/CheckboxOrRadioGroup'; import SingleInput from '../components/SingleInput'; import TextArea from '../components/TextArea'; import Select from '../components/Select'; class FormContainer extends Component { constructor(props) { super(props); this.state = { ownerName: '', petSelections: [], selectedPets: [], ageOptions: [], ownerAgeRangeSelection: '', siblingOptions: [], siblingSelection: [], currentPetCount: 0, description: '' }; this.handleFormSubmit = this.handleFormSubmit.bind(this); this.handleClearForm = this.handleClearForm.bind(this); this.handleFullNameChange = this.handleFullNameChange.bind(this); this.handleCurrentPetCountChange = this.handleCurrentPetCountChange.bind(this); this.handleAgeRangeSelect = this.handleAgeRangeSelect.bind(this); this.handlePetSelection = this.handlePetSelection.bind(this); this.handleSiblingsSelection = this.handleSiblingsSelection.bind(this); this.handleDescriptionChange = this.handleDescriptionChange.bind(this); } componentDidMount() { // 模擬請求用戶數據 //(create-react-app 構建配置裏包含了 fetch 的 polyfill) fetch('./fake_db.json') .then(res => res.json()) .then(data => { this.setState({ ownerName: data.ownerName, petSelections: data.petSelections, selectedPets: data.selectedPets, ageOptions: data.ageOptions, ownerAgeRangeSelection: data.ownerAgeRangeSelection, siblingOptions: data.siblingOptions, siblingSelection: data.siblingSelection, currentPetCount: data.currentPetCount, description: data.description }); }); } handleFullNameChange(e) { this.setState({ ownerName: e.target.value }); } handleCurrentPetCountChange(e) { this.setState({ currentPetCount: e.target.value }); } handleAgeRangeSelect(e) { this.setState({ ownerAgeRangeSelection: e.target.value }); } handlePetSelection(e) { const newSelection = e.target.value; let newSelectionArray; if(this.state.selectedPets.indexOf(newSelection) > -1) { newSelectionArray = this.state.selectedPets.filter(s => s !== newSelection) } else { newSelectionArray = [...this.state.selectedPets, newSelection]; } this.setState({ selectedPets: newSelectionArray }); } handleSiblingsSelection(e) { this.setState({ siblingSelection: [e.target.value] }); } handleDescriptionChange(e) { this.setState({ description: e.target.value }); } handleClearForm(e) { e.preventDefault(); this.setState({ ownerName: '', selectedPets: [], ownerAgeRangeSelection: '', siblingSelection: [], currentPetCount: 0, description: '' }); } handleFormSubmit(e) { e.preventDefault(); const formPayload = { ownerName: this.state.ownerName, selectedPets: this.state.selectedPets, ownerAgeRangeSelection: this.state.ownerAgeRangeSelection, siblingSelection: this.state.siblingSelection, currentPetCount: this.state.currentPetCount, description: this.state.description }; console.log('Send this in a POST request:', formPayload) this.handleClearForm(e); } render() { return ( <form className="container" onSubmit={this.handleFormSubmit}> <h5>Pet Adoption Form</h5> <SingleInput inputType={'text'} title={'Full name'} name={'name'} controlFunc={this.handleFullNameChange} content={this.state.ownerName} placeholder={'Type first and last name here'} /> <Select name={'ageRange'} placeholder={'Choose your age range'} controlFunc={this.handleAgeRangeSelect} options={this.state.ageOptions} selectedOption={this.state.ownerAgeRangeSelection} /> <CheckboxOrRadioGroup title={'Which kinds of pets would you like to adopt?'} setName={'pets'} type={'checkbox'} controlFunc={this.handlePetSelection} options={this.state.petSelections} selectedOptions={this.state.selectedPets} /> <CheckboxOrRadioGroup title={'Are you willing to adopt more than one pet if we have siblings for adoption?'} setName={'siblings'} controlFunc={this.handleSiblingsSelection} type={'radio'} options={this.state.siblingOptions} selectedOptions={this.state.siblingSelection} /> <SingleInput inputType={'number'} title={'How many pets do you currently own?'} name={'currentPetCount'} controlFunc={this.handleCurrentPetCountChange} content={this.state.currentPetCount} placeholder={'Enter number of current pets'} /> <TextArea title={'If you currently own pets, please write their names, breeds, and an outline of their personalities.'} rows={5} resize={false} content={this.state.description} name={'currentPetInfo'} controlFunc={this.handleDescriptionChange} placeholder={'Please be thorough in your descriptions'} /> <input type="submit" className="btn btn-primary float-right" value="Submit"/> <button className="btn btn-link float-left" onClick={this.handleClearForm}>Clear form</button> </form> ); } } export default FormContainer;
我認可用 React 構建受控表單組件要作一些重複勞動(好比容器組件中的處理方法),但就你對應用的掌控度和 state 變動的透明度來講,預先投入精力是超值的。你的代碼會變得可維護而且很高效。
若是想在我發佈新文章時接到通知,你能夠在博客的導航欄部分註冊個人郵件發送清單。
iKcamp原創新書《移動Web前端高效開發實戰》已在亞馬遜、京東、噹噹開售。