表單
提到React中表單及事件處理,就不得不先介紹一下控組件與非受控組件的概念。
在HTML中,表單元素與其他元素最大的不同是它自帶值或數(shù)據(jù),而且在我們的應(yīng)用中,只要是有表單出現(xiàn)的地方,就會有用戶輸入,就會有表單事件觸發(fā),就會涉及的數(shù)據(jù)處理。
在我們用React開發(fā)應(yīng)用時,為了更好地管理應(yīng)用中的數(shù)據(jù),響應(yīng)用戶的輸入,編寫組件的時候呢,我們就會運(yùn)用到受控組件與非受控組件這兩個概念。
React推薦我們在絕大多數(shù)情況下都使用受控組件。這樣可以保證表單的數(shù)據(jù)在組件的state管理之下,而不是各自獨立保有各自的數(shù)據(jù)。
受控與非受控組件 Controlled & Uncontrolled
受控組件:
一般涉及到表單元素時我們才會使用這種分類方法。受控組件的值由props或state傳入,用戶在元素上交互或輸入內(nèi)容會引起應(yīng)用state的改變。在state改變之后重新渲染組件,我們才能在頁面中看到元素中值的變化,假如組件沒有綁定事件處理函數(shù)改變state,用戶的輸入是不會起到任何效果的,這也就是“受控”的含義所在。
非受控組件:
類似于傳統(tǒng)的DOM表單控件,用戶輸入不會直接引起應(yīng)用state的變化,我們也不會直接為非受控組件傳入值。想要獲取非受控組件,我們需要使用一個特殊的ref屬性,同樣也可以使用defaultValue屬性來為其指定一次性的默認(rèn)值。
來看具體的例子:
// 受控組件
class ControlledInput extends React.Component {
constructor() {
super()
this.state = {value: 'Please type here...'}
}
handleChange(event) {
console.log('Controlled change:',event.target.value)
this.setState({value: event.target.value})
}
render() {
return (
<label>
Controlled Component:
<input type="text"
value={this.state.value}
onChange={(e) => this.handleChange(e)}
/>
</label>
)
}
}
// 非受控組件
class UncontrolledInput extends React.Component {
constructor() {
super()
}
handleChange() {
console.log('Uncontrolled change:',this.input.value)
}
render() {
return (
<label>
Uncontrolled Component:
<input type="text"
defaultValue='Please type here...'
ref={(input) => this.input = input}
onChange={() =>this.handleChange()}
/>
</label>
)
}
}
ReactDOM.render(
<div>
<UncontrolledInput />
<ControlledInput />
</div>
,document.getElementById('root'))
通常情況下,React當(dāng)中所有的表單控件都需要是受控組件。但正如我們對受控組件的定義,想讓受控組件正常工作,每一個受控組件我們都需要為其編寫事件處理函數(shù),有的時候確實會很煩人,比方說一個注冊表單你需要寫出所有驗證姓名電話郵箱驗證碼的邏輯,當(dāng)然也有一些小技巧可以讓同一個事件處理函數(shù)應(yīng)用在多個表單組件上,但生產(chǎn)開發(fā)中并沒有多大實際意義。更有可能我們是在對已有的項目進(jìn)行重構(gòu),除了React之外還有一些別的庫需要和表單交互,這時候使用非受控組件可能會更方便一些。
表單元素
我們在組件中聲明表單元素時,一般都要為表單元素傳入應(yīng)用狀態(tài)中的值,可以通過state也可以通過props傳遞,之后需要為其綁定相關(guān)事件,例如表單提交,輸入改變等。在相關(guān)事件觸發(fā)的處理函數(shù)中,我們需要根據(jù)表單元素中用戶的輸入,對應(yīng)用數(shù)據(jù)進(jìn)行相應(yīng)的操作和改變,來看下面這個例子:
class ControlledInput extends React.Component {
constructor(props) {
super(props)
this.state = {
value: ""
}
}
handleChange(event) {
this.setState({
value: event.target.value
})
}
render() {
return <input
type="text"
value={this.state.value}
onChange={ (e) => this.handleChange(e)}
/>
}
}
ReactDOM.render(<ControlledInput />,document.getElementById('root'))
受控組件的輸入數(shù)據(jù)是一直和我們的應(yīng)用狀態(tài)綁定的,在上面這個例子中,事件處理函數(shù)中一定要有關(guān)state的更新操作,這樣表單組件才能及時正確響應(yīng)用戶的輸入,可以把setState語句注釋掉來試驗一下。
textarea:
HTML
<textarea>
Hello there, this is some text in a text area
</textarea>
JSX
<textarea value={this.state.value} onChange={this.handleChange} />
這里需要強(qiáng)調(diào)一下,JSX中使用的和HTML標(biāo)簽同名的元素并不等同于原生的HTML標(biāo)簽,這只是React內(nèi)部抽象出來的一種標(biāo)簽的寫法,只是看起來一樣而已,下面就介紹一下表單元素中,JSX和HTML不一樣的,需要注意的地方。
在HTML中,textarea標(biāo)簽當(dāng)中的內(nèi)容都是在其開閉合標(biāo)簽之間的子節(jié)點當(dāng)中的。而在JSX中,為了統(tǒng)一,textarea也可以定義一個名為value的屬性,用來傳入應(yīng)用狀態(tài)中的相關(guān)值。
select
HTML
<select>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option selected value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
JSX
<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>
select也是一樣,注意這里的寫法,同樣我們可以為JSX當(dāng)中的select標(biāo)簽定義value屬性,與應(yīng)用狀態(tài)中相關(guān)數(shù)據(jù)值相同的option將會被默認(rèn)選中。
使用受控組件和非受控組件都是有響應(yīng)的適用場景的,就拿input來講,比方說它是一個搜索框,我們需要在應(yīng)用中實現(xiàn)根據(jù)搜索框內(nèi)容輸入異步返回相關(guān)搜索建議的功能,那么此處的input就應(yīng)該是受控組件。而假如它是Todo應(yīng)用中用來添加新事項的輸入框,我們就沒有特別的理由需要實時獲取其中的數(shù)據(jù),只需要在添加事項的事件觸發(fā)時獲取輸入框中的值即可,這個地方就可以使用非受控組件。
事件
HTML
<button onclick="activateLasers()">
Activate Lasers
</button>
JSX
<button onClick={activateLasers}>
Activate Lasers
</button>
React元素的事件屬性幾乎與HTML中的事件相關(guān)屬性相同,不過在React當(dāng)中,事件相關(guān)的屬性是以小駝峰的方式命名的。在這里還是要強(qiáng)調(diào)一下,React元素中的事件處理也是React內(nèi)部的抽象封裝,這里只是說,我們在JSX中寫出來,看上去差不多,并不代表這是HTML原生的事件屬性
// 手動綁定
this.handleClick = this.handleClick.bind(this);
// 箭頭函數(shù)自動綁定
handleClick = () => {
console.log('this is:', this);
}
新版本的React中,我們可以通過類和函數(shù)聲明React組件,在這兩種形式的聲明當(dāng)中,我們都可以為其定義事件處理函數(shù),函數(shù)定義的組件只需要在其方法內(nèi)部再定義事件觸發(fā)的函數(shù)即可;而如果是類聲明組件,類定義組件中的自定義方法默認(rèn)是沒有綁定this的,因此加入我們需要在事件處理函數(shù)中調(diào)用this.setState一類的方法,就必須要手動將this綁定在相應(yīng)的事件處理函數(shù)上。
代碼示例:
class ControlledInput extends React.Component {
constructor(props) {
super(props)
this.state = {
value: ""
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
value: event.target.value
})
}
render() {
return <input
type="text"
value={this.state.value}
onChange={ this.handleChange}
/>
}
}
ReactDOM.render(<ControlledInput />,document.getElementById('root'))