原文連接:reactjs.org/docs/forms.…html
React中表單元素與其餘元素的有些不一樣,由於表單元素自己就是帶有狀態的。如今讓咱們來看看下面這段代碼:react
<form>
<label>
Name:
<input type="text" name="name" />
</label>
<input type="submit" value="Submit" />
</form>
複製代碼
這個表單帶有HTML自帶的默認行爲:當用戶點擊submit
時,瀏覽器進入一個新的頁面。在React中,表單是默認帶有這一行爲的。但在大多數狀況下,咱們須要一個JavaScript函數來處理用戶輸入的數據和提交事件。實現這種方式的標準方法是使用受控組件。數組
在HTML中,表單元素如<input>
,<textarea>
,<select>
自身就帶有狀態並根據用戶輸入來更新自身的UI。可是在React中,可變的狀態一般保存在組件的state
屬性中,而且只能經過setState()
來更新state。瀏覽器
咱們能夠將二者結合起來,使React的state成爲「惟一數據源」。渲染表單的組件還控制着用戶輸入數據時表單的操做,以這種方式被React控制輸入值的表單元素被稱爲受控組件。bash
若是咱們想在上一個例子的代碼中在用戶提交時將用戶輸入的姓名打印出來,咱們能夠將表單寫爲受控組件:服務器
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('提交的名字: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
名字:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="提交" />
</form>
);
}
}
複製代碼
因爲咱們在表單元素上設置了value
屬性,如今展現的輸入值就一直都是this.state.value
的值,這就讓React的state
成爲了惟一數據源。由於伴隨着每次用戶敲擊鍵盤輸入數據,handleChange
函數也被調用去更新state,因此展現的數據就像用戶寫入的同樣更新。函數
在受控組件中,狀態的改動都關聯着一個處理函數,這使得修改或者校驗用戶輸入變得簡單。好比咱們想要用戶輸入的姓名都是大寫英文字母,咱們能夠這樣改寫handleChange
:學習
handleChange(event) {
this.setState({value: event.target.value.toUpperCase()});
}
複製代碼
在HTML中,<textarea>
標籤的文本由它的子元素決定。ui
<textarea>
Hello there, this is some text in a text area
</textarea>
複製代碼
在React中, <textarea>
則用一個value屬性代替子元素。這樣,使用<textarea>
的表單和使用單行輸入的表單就很是類似。this
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>
Essay:
<textarea value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
複製代碼
this.state.value
在構造函數中被初始化,因此text area最開始就是有值的。
在HTML中,<select>
標籤建立了一個下拉列表,下面的代碼表示了一個香料的下拉列表:
<select>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option selected value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
複製代碼
能夠發現,Coconut選項上由selected
屬性,因此這個選擇是默認選擇的。然而在React中,咱們使用在<select>
標籤上的value
屬性代替在<option>
上的selected
屬性。這在受控組件裏是很是方便的由於咱們只須要在一個地方更新數據就能夠了。
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 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>
);
}
}
複製代碼
總的來講,這種方式使得<input type="text">
, <textarea>
和 <select>
的工做方式十分類似:都接收一個value屬性。你可使用它們來實現受控組件。
提示 你能夠在
<select>
標籤的value屬性上傳遞一個數組,這樣就能夠選擇多行選項。<select multiple={true} value={['B', 'C']}>
在HTML中,<input type="file">
標籤讓用戶從他們的設備上選擇一個或多個文件上傳到服務器或由JavaScript 文件API操做。
<input type="file" />
複製代碼
由於它的狀態是隻讀的,因此在React中它是一個非受控組件。它將會和其餘非受控組件一塊兒在後面的章節裏討論。
當你須要處理多個受控的input元素時,你能夠給每一個input元素添加一個name
屬性,並讓處理函數根據event.target.name
的值來選擇如何操做。
例如:
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計算屬性名稱
來根據name屬性更新對應key的state。
this.setState({
[name]: value
});
複製代碼
這與下面ES5代碼是等價的:
var partialState = {};
partialState[name] = value;
this.setState(partialState);
複製代碼
另外,因爲setState()
會自動合併state到當前的state,因此咱們只須要調用它來更改部分state便可。
在受控組件上指定value的值能夠阻止用戶更改輸入。可是若是你指定了value的值但輸入依然能夠被更改,那麼有多是你將value
值設置爲undefined
或null
。
下面的代碼展現了這種狀況。最初input是被鎖定的,但過了一會就變成可更改的了。
ReactDOM.render(<input value="hi" />, mountNode);
setTimeout(function() {
ReactDOM.render(<input value={null} />, mountNode);
}, 1000);
複製代碼
使用受控組件可能會讓你決定單調。由於你須要爲每個數據輸入編寫處理函數,將全部的輸入狀態綁定到組件中。當你要將原先的項目轉化成Reac應用或將React應用改寫成非React應用時就會讓人很是煩躁了。在這種狀況下,你可使用一種替代技術來實現表單,非受控組件。
若是你正在尋找一個可以提供校驗,跟蹤訪問字段和處理提交事件功能的解決方案,那麼Formik就是你最好的選擇。Formik時創建在受控組件和狀態管理的原則上的,因此請不要忘記學習它喲。