背景
React內部分別使用了props, state來區分組件的屬性和狀態。props用來定義組件外部傳進來的屬性, 屬於那種經過外部定義之后, 組件內部就無法改變。而state維持組件內部的狀態更新和變化, 組件渲染出來后響應用戶的一些操作,更新組件的一些狀態。如果組件內部狀態不需要更新,即沒有調用過this.setState
, 全部通過props來渲染也是沒問題的, 不過這種情況不常見。本文所介紹的內容就是通過props和state的定義來談談React的受控組件和非受控組件。
非受控組件
顧名思義, 非受控組件即組件的狀態改變不受控制.接來下我們以一個簡單input組件代碼來描述。
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class Demo1 extends Component {
render() {
return (
<input />
)
}
}
ReactDOM.render(<Demo1/>, document.getElementById('content'))
在這個最簡單的輸入框組件里,我們並沒有干涉input中的value展示,即用戶輸入的內容都會展示在上面。如果我們通過props給組件設置一個初始默認值,<input defaultValue={this.props.value}/>
defaultValue屬性是React內部實現的一個屬性,目的類似於input的placeholder屬性。
ps: 此處如果使用value代替defaultValue,會發現輸入框的值無法改變。
受控組件
上面提到過,既然通過設置input的value屬性, 無法改變輸入框值,那么我們把它和state結合在一起,再綁定onChange事件,實時更新value值就行了。
class Demo1 extends Component {
constructor(props) {
super(props);
this.state = {
value: props.value
}
}
handleChange(e) {
this.setState({
value: e.target.value
})
}
render() {
return (
<input value={this.state.value} onChange={e => this.handleChange(e)}/>
)
}
}
這就是最簡單的受控組件模型, 我們可以通過在onChange的回調里控制input要顯示的值,例如我們設置input框只能輸入數字
this.setState({
value: e.target.value.replace(/\D/g, '')
})
現在我們應該完全明白form表單中受控組件和非受控組件的關系。受控組件采取的理念類似於redux的單項數據流理念,即value值是在調用者上更新的。
那么問題來了。。。
最后的思考
現在我們要實現一個簡單的input的number類型組件,后面緊跟一個+的button按鈕,將輸入框內的數字每次加一。所以此處只能按照受控組件的理念來寫
import React, { Component } from 'react';
export default class extends Component {
constructor(props) {
super(props);
this.state = {
value: props.value
}
}
handleChange(e) {
this.setState({
value: e.target.value.replace(/\D/g, '')
})
}
plus() {
const value = ++this.input.value
this.setState({
value,
})
}
render() {
return (
<div>
<input
value={this.state.value}
onChange={e => this.handleChange(e)}
ref={ref => this.input = ref}
/>
<button onClick={() => this.plus()}>+</button>
</div>
)
}
}
此處功能基本實現完全,但是發現使用此組件之后,面臨了另一個問題,我們如何在外部獲取到這個輸入框的值。一種方法是給組件增加個getValue回調的props,每次value值變化都調用一次getValue,即在handleChange和plus函數里調用,但是存在一個問題是,調用者只能通過getValue被動獲取值,而且value值得改變此時還是在組件內部自行變化,不符合受控組件原理,也不滿足React單向數據流概念。另一種方法就是將input組件的將要改變的值傳到調用者里面,由調用者來決定更不更新組件的值,即此時數據由被調用者input組件生成,傳至調用者,調用者判斷滿足條件后決定更新,再將數據重新傳入到被調用者里。而調用者與被調用者彼此之間建立的聯系方式通過input組件的props和調用者的state。此時input組件的代碼如下
export default class extends Component {
constructor(props) {
super(props);
this.state = {
value: props.value
}
}
componentWillReceiveProps(nextProps) {
this.setState({
value: nextProps.value
})
}
handleChange(e) {
this.props.onChange(e.target.value)
}
plus() {
const value = ++this.input.value
this.props.onChange(value)
}
render() {
return (
<div>
<input
value={this.state.value}
onChange={e => this.handleChange(e)}
ref={ref => this.input = ref}
/>
<button onClick={() => this.plus()}>+</button>
</div>
)
}
}
代碼中的this.props.onChange就是調用者內部的函數,通過setState來更新input組件的value值。完整代碼已放到github,歡迎指正交流。