新手寫 React 組件往往無從入手,怎么寫,什么時(shí)候用 props,什么時(shí)候用 state 摸不著頭腦。其實(shí)是沒有了解到 React 的一些思想。就我個(gè)人的經(jīng)驗(yàn)大多數(shù)的組件都有一定的套路可言,接下來就先介紹下 React 組件的基本思想。
React 組件可以分為可控組件和非可控組件。可控組件意思是組件自身控制自己的狀態(tài)(屬性),可以通過自身提供的方法(供調(diào)用者使用)來改變自己的狀態(tài)。譬如一個(gè) input text 輸入框提供一個(gè) reset 方法,如果要清空用戶輸入則通過獲得 inupt 組件對(duì)象,然后調(diào)用 reset 方法來做
refs.inputRef.rest() 。
非可控組件的意思是組件本身的狀態(tài)(屬性)自己無法更改,只能隨著外部傳入的值(props)而變化。還是拿輸入框清空這一個(gè)操作來說,非可控的 input 不通過自己提供方法來改變(維護(hù))自己的狀態(tài)(value),只通過外部傳入一個(gè)值為空字符串的 value 來做到清空的效果。
reset(){
this.setState({
inputValue: ''
})
}
render(){
return <input value={this.state.inputValue}/>
}
我們拿一個(gè)場(chǎng)景來看下完整的代碼(一個(gè) form 中有一個(gè) input,有一個(gè) reset 按妞,點(diǎn)擊 reset 按妞會(huì)清空用戶的輸入),看下這兩種組件書寫的區(qū)別。
受控組件例:
class App extends React.Component {
reset = ()=>{
this.refs.myInput.reset() // 假設(shè) input 有一個(gè) reset 方法
}
render() {
<div>
<form>
<input type="text" ref="myInput" />
<button onClick={ this.reset }>Reset</button>
</form>
</div>
}
}
非受控組件例:
class App extends React.Component {
constructor( props ){
super( props );
this.state = {
inputValue: 'Plz input your text.'
}
}
reset = ()=>{
this.setState( {
inputValue: ''
} )
}
render() {
<div>
<form ref="myForm">
<input type="text" value={ this.state.inputValue }/>
<button onClick={ this.reset }>Reset</button>
</form>
</div>
}
}
接下來我們來看下如果編寫這兩種組件,打個(gè)比方我們要自定義一個(gè) alert 組件。我們先從非受控組件說起,因?yàn)檩^簡(jiǎn)單。非受控組件所要做的就是把所有狀態(tài)提取到組件的 props 中去,render 中就用 props。一個(gè) alert 有哪些最基本的狀態(tài)(屬性)呢?我們以最基礎(chǔ)的功能定出一個(gè)表示顯示與否的 show,一個(gè)表示顯示內(nèi)容的 content。那么組件代碼如下。
class Alert extends React.Component {
constructor( props ) {
super( props )
}
render() {
let style = {
display: this.props.show ? 'fixed' : 'none'
}
return (
<div class="my-alert" style={ style } >
<div class="my-alert-tit">Alert</div>
<div>{ this.props.content }</div>
<div class="my-alert-footer">
<button>確定</button>
</div>
</div>
);
}
}
Alert.propTypes = {
show: React.PropTypes.bool,
content: React.PropTypes.string
}
我們看到最直觀的就是只需要考慮到 props 的可能取值就行,不需要關(guān)心如何改變props。而使用這個(gè)非可控 alert 的代碼如下:
class App extends React.Component {
constructor() {
super();
this.state = {
alertMsg: '',
showAlert: false
}
this.saveHandler = ()=>{
// ajax success
this.setState( {
alertMsg: 'Save successfully',
showAlert: true
} )
}
}
render() {
<div>
<button onClick={ this.saveHandler }>Save</button>
<Alert
content={ this.state.alertMsg }
show={ this.state.showAlert }
/>
</div>
}
}
接下來我們看下可控組件的alert怎么寫。可控組件通過方法來供調(diào)用者來改變組件的狀態(tài)(屬性)。所以暫時(shí)我們不定義 props 只定義幾個(gè)方法 show(content), hide()。組件代碼如下:
class Alert extends React.Component {
constructor( props ) {
super( props )
this.state = {
content: '',
show: false
}
this.show = ( content )=>{
this.setState( {
content: content,
show: true
} )
}
this.hide = ()=>{
this.setState( {
show: false
} )
}
}
render() {
let style = {
display: this.state.show ? 'fixed' : 'none'
}
return (
<div class="my-alert" style={ style } >
<div class="my-alert-tit">Alert</div>
<div>{ this.state.content }</div>
<div class="my-alert-footer">
<button onClick={ this.hide }>確定</button>
</div>
</div>
);
}
}
我們看到可控組件內(nèi)部需要用到 state 來自己改變自己的狀態(tài)。使用這個(gè)可控 alert 的代碼如下:
import { Alert } from 'Alert';
class App extends React.Component {
constructor() {
super();
this.saveHandler = ()=>{
// ajax success
this.refs.myAlert.show( 'Save Successfully' );
}
}
render() {
<div>
<button onClick={ this.saveHandler }>Save</button>
<Alert ref="myAlert"/>
</div>
}
}
但是可控組件有一個(gè)問題就是他的初始化狀態(tài)如何設(shè)置(如何由外部定義組件 state 的初始化值)?由于沒有 props 那么只能通過方法來設(shè)置,那么這么做法很別扭。這時(shí)可以通過定義 props 把初始化狀態(tài)在生成這個(gè)組件時(shí)傳入,而不必等組件生成完再通過調(diào)用方法傳入。于是修改后的代碼如下:
class Alert extends React.Component {
constructor( props ) {
super( props )
this.state = {
content: this.props.defaultContent,
show: this.props.defaultShow
}
this.show = ( content )=>{
this.setState( {
content: content,
show: true
} )
}
this.hide = ()=>{
this.setState( {
show: false
} )
}
}
render() {
let style = {
display: this.state.show ? 'fixed' : 'none'
}
return (
<div class="my-alert" style={ style } >
<div class="my-alert-tit">Alert</div>
<div>{ this.state.content }</div>
<div class="my-alert-footer">
<button onClick={ this.hide }>確定</button>
</div>
</div>
);
}
}
Alert.propTypes = {
defaultShow: React.PropTypes.bool,
defaultContent: React.PropTypes.string
}
Alert.defaultProps = {
defaultShow: false,
defaultContent: ''
}
使用這個(gè)組件的代碼:
class App extends React.Component {
constructor() {
super();
this.state = {
alertMsg: '',
showAlert: false
}
this.saveHandler = ()=>{
// ajax success
this.refs.myAlert.show( 'Save Successfully' );
}
}
render() {
<div>
<button onClick={ this.saveHandler }>Save</button>
<Alert ref="myAlert" defaultShow={false} defaultContent={''}/>
</div>
}
}
以上就是兩種 React 組件的編寫思路,你可以選擇把你的組件編寫成任意一種,那么使用者使用時(shí)也會(huì)有所不同。但是作為一個(gè)具有良好可用性的組件,不應(yīng)該限制使用者的用法,那么下篇將介紹如何編寫一個(gè)既可以作為可控組件,也可以作為一個(gè)非可控組件的組件寫法。