表單
提到React中表單及事件處理祝谚,就不得不先介紹一下控組件與非受控組件的概念。
在HTML中酣衷,表單元素與其他元素最大的不同是它自帶值或數(shù)據(jù)交惯,而且在我們的應(yīng)用中,只要是有表單出現(xiàn)的地方穿仪,就會(huì)有用戶輸入席爽,就會(huì)有表單事件觸發(fā),就會(huì)涉及的數(shù)據(jù)處理啊片。
在我們用React開發(fā)應(yīng)用時(shí)只锻,為了更好地管理應(yīng)用中的數(shù)據(jù),響應(yīng)用戶的輸入紫谷,編寫組件的時(shí)候呢齐饮,我們就會(huì)運(yùn)用到受控組件與非受控組件這兩個(gè)概念。
React推薦我們?cè)诮^大多數(shù)情況下都使用受控組件笤昨。這樣可以保證表單的數(shù)據(jù)在組件的state管理之下祖驱,而不是各自獨(dú)立保有各自的數(shù)據(jù)。
受控與非受控組件 Controlled & Uncontrolled
受控組件:
一般涉及到表單元素時(shí)我們才會(huì)使用這種分類方法瞒窒。受控組件的值由props或state傳入羹膳,用戶在元素上交互或輸入內(nèi)容會(huì)引起應(yīng)用state的改變。在state改變之后重新渲染組件根竿,我們才能在頁面中看到元素中值的變化陵像,假如組件沒有綁定事件處理函數(shù)改變state,用戶的輸入是不會(huì)起到任何效果的寇壳,這也就是“受控”的含義所在醒颖。
非受控組件:
類似于傳統(tǒng)的DOM表單控件,用戶輸入不會(huì)直接引起應(yīng)用state的變化壳炎,我們也不會(huì)直接為非受控組件傳入值泞歉。想要獲取非受控組件,我們需要使用一個(gè)特殊的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)中所有的表單控件都需要是受控組件铲球。但正如我們對(duì)受控組件的定義挺庞,想讓受控組件正常工作,每一個(gè)受控組件我們都需要為其編寫事件處理函數(shù)稼病,有的時(shí)候確實(shí)會(huì)很煩人选侨,比方說一個(gè)注冊(cè)表單你需要寫出所有驗(yàn)證姓名電話郵箱驗(yàn)證碼的邏輯,當(dāng)然也有一些小技巧可以讓同一個(gè)事件處理函數(shù)應(yīng)用在多個(gè)表單組件上然走,但生產(chǎn)開發(fā)中并沒有多大實(shí)際意義援制。更有可能我們是在對(duì)已有的項(xiàng)目進(jìn)行重構(gòu),除了React之外還有一些別的庫需要和表單交互芍瑞,這時(shí)候使用非受控組件可能會(huì)更方便一些晨仑。
表單元素
我們?cè)诮M件中聲明表單元素時(shí),一般都要為表單元素傳入應(yīng)用狀態(tài)中的值拆檬,可以通過state也可以通過props傳遞洪己,之后需要為其綁定相關(guān)事件,例如表單提交秩仆,輸入改變等码泛。在相關(guān)事件觸發(fā)的處理函數(shù)中,我們需要根據(jù)表單元素中用戶的輸入澄耍,對(duì)應(yīng)用數(shù)據(jù)進(jìn)行相應(yīng)的操作和改變噪珊,來看下面這個(gè)例子:
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)綁定的,在上面這個(gè)例子中齐莲,事件處理函數(shù)中一定要有關(guān)state的更新操作痢站,這樣表單組件才能及時(shí)正確響應(yīng)用戶的輸入,可以把setState語句注釋掉來試驗(yàn)一下选酗。
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é)點(diǎn)當(dāng)中的。而在JSX中娱颊,為了統(tǒng)一傲诵,textarea也可以定義一個(gè)名為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將會(huì)被默認(rèn)選中栓拜。
使用受控組件和非受控組件都是有響應(yīng)的適用場(chǎng)景的,就拿input來講斑响,比方說它是一個(gè)搜索框菱属,我們需要在應(yīng)用中實(shí)現(xiàn)根據(jù)搜索框內(nèi)容輸入異步返回相關(guān)搜索建議的功能,那么此處的input就應(yīng)該是受控組件舰罚。而假如它是Todo應(yīng)用中用來添加新事項(xiàng)的輸入框纽门,我們就沒有特別的理由需要實(shí)時(shí)獲取其中的數(shù)據(jù),只需要在添加事項(xiàng)的事件觸發(fā)時(shí)獲取輸入框中的值即可营罢,這個(gè)地方就可以使用非受控組件赏陵。
事件
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)部的抽象封裝考传,這里只是說吃型,我們?cè)贘SX中寫出來,看上去差不多僚楞,并不代表這是HTML原生的事件屬性
// 手動(dòng)綁定
this.handleClick = this.handleClick.bind(this);
// 箭頭函數(shù)自動(dòng)綁定
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一類的方法,就必須要手動(dòng)將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'))