state
Component的顯示形態(tài)是有它的數(shù)據(jù)狀態(tài)和參數(shù)決定的一罩。React.js的state就是用來(lái)存儲(chǔ)這種可變化的狀態(tài)。
例如以下代碼:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import './index.css'
class LikeButton extends Component {
constructor () {
super()
this.state = { isLiked: false }
}
handleClickOnLikeButton () {
this.setState({
isLiked: !this.state.isLiked
})
}
render () {
return (
<button onClick={this.handleClickOnLikeButton.bind(this)}>
{this.state.isLiked ? '取消' : '點(diǎn)贊'}
</button>
)
}
}
···
isLiked被存放在實(shí)例的state對(duì)象中植榕,這個(gè)對(duì)象在構(gòu)造函數(shù)里被初始化。這個(gè)Component的render方法里坯认,會(huì)根據(jù)state中isLiked的不同顯示‘取消’或者‘點(diǎn)贊’盾似,并且button加上了點(diǎn)擊的事件監(jiān)聽(tīng)。
在 handleClickOnLikeButton 事件監(jiān)聽(tīng)方法中皆看,使用setState方法仓坞,每次點(diǎn)擊會(huì)更新isLiked的屬性為!isLiked,這樣就完成了點(diǎn)贊和取消腰吟。
setState方法是由父類(lèi)Component提供的无埃。當(dāng)我們調(diào)用這個(gè)函數(shù)的時(shí)候,React.js會(huì)更新組件的狀態(tài)state毛雇,并且重新調(diào)用render方法嫉称,更新顯示頁(yè)面內(nèi)容。
注意灵疮,不能直接使用 this.state = xxx 的方式來(lái)修改织阅,如果這樣做React.js就沒(méi)法知道你修改了的組件的狀態(tài),也就沒(méi)法刷新頁(yè)面震捣。所以荔棉,一定要使用setState的方法,它接受一個(gè)對(duì)象或者函數(shù)作為參數(shù)蒿赢。
傳入一個(gè)對(duì)象的時(shí)候润樱,只需要傳遞要更新的屬性就可以了。例如:
constructor (props) {
super(props)
this.state = {
name: 'Sam',
isLiked: false
}
}
handleClickOnLikeButton () {
this.setState({
isLiked: !this.state.isLiked
})
}
handleClickOnLikedButton方法里不需要改嗎state中的name屬性羡棵,因此在setState方法中是需要傳入isLiked屬性就可以了壹若。
setState接受函數(shù)作為參數(shù)
當(dāng)你調(diào)用setState方法時(shí),React.js并不會(huì)馬上修改state皂冰,而是把這個(gè)對(duì)象放到一個(gè)更新隊(duì)列中店展,稍后才會(huì)從隊(duì)列中把新的狀態(tài)提取出來(lái)合并到state當(dāng)中,然后再出發(fā)組件更新秃流。這一點(diǎn)要特別注意赂蕴,可以體會(huì)一下以下代碼:
handleClickOnLikeButton () {
console.log(this.state.isLiked)//false
this.setState({
isLiked: !this.state.isLiked
})
console.log(this.state.isLiked)//false
}
此時(shí)控制臺(tái)會(huì)輸出兩次false,即使中間調(diào)用了一次setState方法舶胀,也沒(méi)有什么bug概说,但是isLiked得值并沒(méi)有改變。只是React.js得setState把傳進(jìn)來(lái)的狀態(tài)緩存了峻贮,沒(méi)有立即更新席怪,所以第二次打印時(shí),還是原來(lái)的isLiked纤控。
所以如果想在setState之后使用新的state來(lái)做后續(xù)的運(yùn)算就做不到了挂捻,例如:
handleClickOnLikeButton () {
this.setState({ count: 0 }) // => this.state.count 還是 undefined
this.setState({ count: this.state.count + 1}) // => undefined + 1 = NaN
this.setState({ count: this.state.count + 2}) // => NaN + 2 = NaN
}
上面的代碼的運(yùn)行結(jié)果并不能達(dá)到我們的預(yù)期,我們希望 count 運(yùn)行結(jié)果是 3 船万,可是最后得到的是 NaN刻撒。但是這種后續(xù)操作依賴(lài)前一個(gè) setState 的結(jié)果的情況并不罕見(jiàn)骨田。
來(lái)看第二種setState方法的第二種使用方式,可以傳入一個(gè)函數(shù)作為參數(shù)声怔。React.js會(huì)把上一個(gè)setState的結(jié)果傳入這個(gè)函數(shù)态贤,你就可以使用該結(jié)果進(jìn)行運(yùn)算、操作醋火,然后返回一個(gè)對(duì)象為更新state對(duì)象:
handleClickOnLikeButton () {
this.setState((prevState) => {
return { count: 0 }
})
this.setState((prevState) => {
return { count: prevState.count + 1 } // 上一個(gè) setState 的返回是 count 為 0悠汽,當(dāng)前返回 1
})
this.setState((prevState) => {
return { count: prevState.count + 2 } // 上一個(gè) setState 的返回是 count 為 1,當(dāng)前返回 3
})
// 最后的結(jié)果是 this.state.count 為 3
}
這樣就可以達(dá)到上述的利用上一次 setState 結(jié)果進(jìn)行運(yùn)算的效果芥驳。
setState合并
上面進(jìn)行了三次setState柿冲,但實(shí)際上組件只會(huì)重新渲染一次,而不是三次兆旬。這是因?yàn)镽eact.js內(nèi)部會(huì)把JavaScript事件循環(huán)中的消息隊(duì)列的同一個(gè)消息中的setState都進(jìn)行合并以后再重新渲染組件假抄。
深層的原理我也沒(méi)搞太清楚,現(xiàn)階段只要記桌鲡:在使用React.js的時(shí)候宿饱,并不需要擔(dān)心多次進(jìn)行setState會(huì)帶來(lái)性能問(wèn)題。