React版本:15.4.2
**翻譯:xiyoki **
在React中,HTML表單元素的工作方式與其他DOM元素的工作方式有點(diǎn)不同。因?yàn)楸韱卧刈匀话恍﹥?nèi)部狀態(tài)。例如,這種采用純HTML格式的表單接受單個(gè)名稱:
<form>
<label>
Name:
<input type="text" name="name" />
</label>
<input type="submit" value="Submit" />
</form>
這個(gè)表單具有在用戶提交表單時(shí)瀏覽到新頁(yè)面的默認(rèn)HTML表單行為。在React中,表單的這一行為也是會(huì)發(fā)揮作用的。但在大多數(shù)情況下,使用Javascript函數(shù)處理表單的提交并訪問(wèn)用戶在表單中輸入的數(shù)據(jù)是很方便的。實(shí)現(xiàn)這一點(diǎn)的標(biāo)準(zhǔn)方法是使用被稱為“受控組件”的技術(shù)。
Controlled Components(受控組件)
在HTML中,表單元素如<input>,<textarea>以及<select>通常保持自己的狀態(tài),并根據(jù)用戶輸入進(jìn)行更新。在React中,可變的狀態(tài)通常保持在組件的state屬性上,并且只能通過(guò)setState()方法進(jìn)行更新。
我們可以通過(guò)把React狀態(tài)變成“唯一真實(shí)的來(lái)源”來(lái)結(jié)合二者。然后,渲染表單的React組件也將控制在用戶輸入后在表單上發(fā)生的事情。其值由React以這種方式進(jìn)行控制的輸入表單元素被稱為“受控組件”。
例如,如果我們想讓上一個(gè)示例在表單被提交時(shí)打印name字段的值,我們可以將表單寫成受控組件。
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
由于value屬性設(shè)置在表單元素上,顯示的值將始終是this.state.value,使得React state成了真實(shí)的來(lái)源。由于handleChange在每次擊鍵后更新React 狀態(tài),顯示的值將更新為用戶類型。
使用受控組件,每個(gè)狀態(tài)變量都具有關(guān)聯(lián)的處理函數(shù)。這便能直接修改或驗(yàn)證用戶輸入。例如,我們強(qiáng)制要Name字段為大寫字母書寫格式,我們可以寫handleChange為:
handleChange(event) {
this.setState({value: event.target.value.toUpperCase()});
}
The textarea Tag(textarea標(biāo)簽)
在HTML中,<textarea>元素的文本由其子元素定義:
<textarea>
Hello there, this is some text in a text area
</textarea>
在React中,<textarea>使用value屬性來(lái)代替。這種方式下,使用了<textarea>的表單的寫法與使用單行輸入框的表單的寫法非常相似:
class EssayForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'Please write an essay about your favorite DOM element.'
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('An essay was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<textarea value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
注意,this.state.value在構(gòu)造函數(shù)中被初始化了,因此文本區(qū)域中是有一些文字的。
The select Tag(選擇標(biāo)簽)
在HTML中,<select>創(chuàng)建一個(gè)下拉列表。例如此HTML創(chuàng)建了一個(gè)下拉列表:
<select>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option selected value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
需要注意的是,Coconut選項(xiàng)最初的時(shí)候被選擇了,因?yàn)?code>selected屬性。在React中,不會(huì)使用selected屬性,而是在根select標(biāo)簽上添加value屬性來(lái)代替。這在受控組件中更方便,因?yàn)槟阒恍枰谝粋€(gè)地方更新它。例如:
class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'coconut'};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('Your favorite flavor is: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Pick your favorite La Croix flavor:
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
總的來(lái)說(shuō), 這樣使得<input type="text">, <textarea>, 和 <select>的工作都相似,它們都接受value屬性,你可以使用它來(lái)實(shí)現(xiàn)一個(gè)受控組件。
Handling Multiple Inputs(處理多個(gè)Inputs)
當(dāng)你需要處理多個(gè)受控的input元素,你可以為每個(gè)元素添加一個(gè)name屬性,讓處理函數(shù)基于event.target.name的屬性值來(lái)選擇后續(xù)該做什么。
例如:
class Reservation extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value; //獲取表單控件的屬性值
const name = target.name;
this.setState({
[name]: value
});
}
render() {
return (
<form>
<label>
Is going:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange}
/>
</label>
<br />
<label>
Number of guests:
<input
name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange}
/>
</label>
</form>
);
}
}
注意,我們是如何使用ES6的 computed property name 語(yǔ)法來(lái)更新對(duì)應(yīng)到給定輸入框名稱的狀態(tài)鍵名的:
this.setState({
[name]: value
});
它等同于如下ES5代碼:
var partialState = {};
partialState[name] = value;
this.setState(partialState);
此外,由于setState()自動(dòng)merges a partial state into the current state,我們只需要在調(diào)用它時(shí)傳入已更改的部分。
Alternatives to Controlled Components(受控組件的替代方案)
使用受控組件有時(shí)候可能很乏味,因?yàn)槟阈枰獮閿?shù)據(jù)能夠更改的每條路徑編寫事件處理程序,并且在React組件中pipe輸入state的全部。當(dāng)將預(yù)先存在的代碼庫(kù)轉(zhuǎn)換為React或?qū)eact應(yīng)用程序與非React庫(kù)集成時(shí),這可能會(huì)變得特別煩人。在這些情況下,你可能要檢查出不受控制的組件,實(shí)現(xiàn)輸入表單的替代技術(shù)。