表單元素是一類擁有內部狀態的元素,這些狀態由其自身維護,經過這類元素可以讓用戶與Web應用進行交互。HTML中的表單元素(例如<input>、<select>和<radio>等)在React中都有相應的組件實現,不只如此,React還將它們分紅兩種:受控組件和非受控組件。html
受控組件(Controlled Component)是指那些受React控制的表單元素,其狀態(value、checked等屬性)的變動由組件的state管理。對於不一樣的表單元素,其受控組件的形式會有所差別,接下來會講解其中的三類。數組
1)文本框dom
經常使用的單行文本框是一個type屬性爲「text」的<input>元素,它的值(即狀態)由value屬性控制。若是要監聽文本框的狀態變化,那麼能夠像下面這樣操做。函數
class Text extends React.Component { constructor(props) { super(props); this.state = {value: "init"}; this.handle = this.handle.bind(this); } handle(e) { this.setState({value: e.target.value.toUpperCase()}); } render() { return <input value={this.state.value} onChange={this.handle} type="text" />; } }
上述代碼實現了一個簡單的功能,在改變文本框中的內容時,自動將其轉換成大寫字母。具體的更新過程可分爲四步:this
(1)在構造函數中初始化組件的state,併爲文本框設置默認值。spa
(2)文本框註冊onChange事件,監聽其值的變化。code
(3)在事件處理程序handle()中,經過e.target.value讀取到輸入的值,修改並同步(調用this.setState()方法)到組件的state中。htm
(4)組件從新渲染,完成文本框的內容更新。對象
其餘兩類受控組件的更新過程與之相似,只是在細節處理上有所不一樣。blog
觀察上面的示例能夠發現,文本框的數據來源於組件的state,經過onChange事件將輸入的新數據再同步給組件的state,從而完成了一次雙向數據綁定。
React中的<textarea>元素(多行文本框),其使用相似於上面的<input>元素,也是經過value屬性來獲取值的,以下代碼所示,省略了構造函數和事件處理程序。
class TextArea extends React.Component { render() { return <textarea value={this.state.value} onChange={this.handle} />; } }
而HTML中的<textarea>元素則會將值定義成子元素,而且包含結束標籤,以下所示。
<textarea>init</textarea>
2)單選框和複選框
單選框是一個type屬性爲「radio」的<input>元素,複選框是一個type屬性爲「checkbox」的<input>元素。與以前的文本框不一樣,React控制的不是它們的值,而是選中狀態,即布爾屬性checked。在下面的例子中,監聽了每一個單選框的checked屬性。
class Radio extends React.Component { constructor(props) { super(props); this.state = { gender: "" }; this.handle = this.handle.bind(this); } handle(e) { this.setState({ gender: e.target.value }); } render() { return ( <> <input name="gender" value="1" onChange={this.handle} type="radio" checked={this.state.gender == "1"} />男 <input name="gender" value="2" onChange={this.handle} type="radio" checked={this.state.gender == "2"} />女 </> ); } }
複選框能選中多個項,其操做要比單選框繁瑣許多。在下面的例子中,不但監聽了每一個複選框的checked屬性,還將處於選中狀態的值提取了出來,組成一個數組。
class Checkbox extends React.Component { constructor(props) { super(props); this.state = { colors: [] }; //保存複選框值的數組 this.handle = this.handle.bind(this); } handle(e) { const { checked, value } = e.target; let { colors } = this.state; if (checked && colors.indexOf(value) == -1) { colors.push(value); //已選中而且數組中未有該值,就在末尾插入 } else { colors = colors.filter(item => item != value); //未選中,就將該值過濾掉 } this.setState({ colors }); } render() { return ( <> <input name="colors" value="1" onChange={this.handle} type="checkbox" checked={this.state.colors.indexOf("1") >= 0} />紅 <input name="colors" value="2" onChange={this.handle} type="checkbox" checked={this.state.colors.indexOf("2") >= 0} />綠 <input name="colors" value="3" onChange={this.handle} type="checkbox" checked={this.state.colors.indexOf("3") >= 0} />藍 </> ); } }
雖然React處理單選框和複選框的方式要比在HTML中複雜一點,可是保證了組件的state是元素狀態的惟一來源,進而讓更新過程更加可靠和可控。
3)選擇框
在HTML中,<select>元素(選擇框)會包含多個用來表示選項的<option>元素,而選中的項會被定義一個selected屬性,以下代碼所示,第二個<option>元素處於選中狀態。
<select> <option value="1">strick</option> <option value="2" selected>freedom</option> <option value="3">jane</option> </select>
在React中,只需對<select>元素定義value屬性就能決定當前的選中項,以下代碼所示,這比用DOM的方式操做選項要簡潔得多。
class Select extends React.Component { constructor(props) { super(props); this.state = { value: "" }; this.handle = this.handle.bind(this); } handle(e) { this.setState({ value: e.target.value }); } render() { return ( <select value={this.state.value} onChange={this.handle}> <option value="1">strick</option> <option value="2">freedom</option> <option value="3">jane</option> </select> ); } }
只要給<select>元素添加multiple屬性並將其賦爲true就能變爲多選,以下代碼所示,此時傳給value屬性的是一個數組。
class MulSelect extends React.Component { constructor(props) { super(props); this.state = { values: [] }; this.handle = this.handle.bind(this); } handle(e) { const { options } = e.target; //options是一個類數組對象 const values = Object.keys(options) //將options的索引組成一個數組 .filter(i => options[i].selected) //過濾出選中項 .map(i => options[i].value); //提取選中項組成新數組 this.setState({ values }); } render() { return ( <select value={this.state.values} onChange={this.handle} multiple={true}> <option value="1">strick</option> <option value="2">freedom</option> <option value="3">jane</option> </select> ); } }
非受控組件(Uncontrolled Component)的定義正好與受控組件的相左,其狀態由本身管理,一般使用ref屬性(第5篇中講解過)獲取表單元素的值。在下面的示例中,文本框在失去焦點時,能自動將其內容轉換成大寫字母。若是用受控組件的形式完成相同的功能,那麼會較爲繁瑣。
class Text extends React.Component { constructor(props) { super(props); this.handle = this.handle.bind(this); } handle() { this.input.value = this.input.value.toUpperCase(); } render() { return <input onBlur={this.handle} type="text" ref={ input => {this.input = input}}/>; } }
在render()方法中,首先爲文本框註冊onBlur事件,而後定義ref屬性,其值是一個回調函數。當組件被掛載時,就會執行該回調函數,而後就能讓this.input指向一個文本框,從而在事件處理程序handle()中就能經過this.input讀取到文本框中的內容。
在React中,有一個表單元素比較特殊,那就是上傳按鈕。它只有非受控組件的形式,由於其值只能由用戶傳入,不能被組件的state所控制。
1)默認值
若是要指定非受控組件的默認值,那麼可經過定義defaultValue或defaultChecked屬性實現,前者適用於文本框、選擇框等元素,後者適用於單選框和複選框。下面的示例分別給文本框和單選框設置了默認值,爲了便於觀察,只放出了關鍵代碼。
class Text extends React.Component { render() { return <input type="text" defaultValue="init"/>; } } class Radio extends React.Component { render() { return ( <> <input name="gender" value="1" type="radio"/>男 <input name="gender" value="2" type="radio" defaultChecked={true}/>女 </> ); } }