因為 setState
在react
中非常重要贮预,所以單獨拎出來整理它的要點
什么是setState盔性?
react
是通過在constructor
構造函數(shù)中的this.state
去定義狀態(tài)的波桩,然后是通過this.setState
去修改狀態(tài)邮辽,那么setState
有什么特點呢?
不可變值
state
中數(shù)據(jù)不可直接使用this.state.xxx = xxx
形式來改變狀態(tài)臀突,這是因為react
的immutable
概念決定的
- 針對基礎類型
直接修改this.state
值類型時欠动,會報警告:Do not mutate state directly. Use setState()
this.setState({
// 使用this.state.count++,會報警告永乌,因為這句直接修改了原count的值:
// Do not mutate state directly. Use setState()
// count: this.state.count++
count: this.state.count + 1,
})
- 針對數(shù)組
雖然使用this.state.list.push(xx)
這種方式修改數(shù)組后具伍,再使用setState
賦給state
不會報錯翅雏,但還是不建議這么使用,我們應該遵循immutable
理念人芽,通過拷貝后的數(shù)組望几,再賦值給state
的
// 建議使用這種,對原數(shù)據(jù)進行拷貝萤厅,在拷貝上修改
const newList = this.state.list.slice()
newList.push(3)
this.setState({
list: newList
})
- 針對對象
使用Object.assign橄抹,或者使用擴展運算符生成一個新對象方式
this.setState({
obj1: Object.assign({}, this.state.obj1, {x: 1}),
obj2: {...this.state.obj2, x: 1}
})
import React, { Component } from 'react'
class SetStateDemo extends Component{
constructor(props) {
super(props)
this.state = {
count: 0,
list: [
{id: 1, age: 1},
{id: 2, age: 2}
],
obj1: {
a: 1
}
}
}
addCount = () => {
// 建議使用這種靴迫,對原數(shù)據(jù)進行拷貝,在拷貝上修改
const newList = this.state.list.slice()
newList.push(3)
this.setState({
// 使用this.state.count++,會報警告楼誓,因為這句直接修改了原count的值:
// Do not mutate state directly. Use setState()
// count: this.state.count++
count: this.state.count + 1矢劲,
list: newList,
obj1: {...this.state.obj1, x: 1}
})
console.log(this.state.count) // 這里是異步的
}
render() {
return (
<div>
<p>{this.state.count}</p>
<button onClick={this.addCount}>累加</button>
</div>
)
}
}
export default SetStateDemo;
setState是同步的嗎
setState
有時是同步的,有時是異步的慌随,不能一概而論,要區(qū)分使用情況來分析:
異步的setState
:
直接使用onXxxx
綁定的事件躺同,使用的setState
是異步的阁猜,如果要實時獲取數(shù)據(jù),需要在setState
第二個參數(shù)回調函數(shù)中獲忍R铡(類似vue
中的nextTick
作用)
constructor(props) {
super(props)
this.state = {
count: 0,
list: [
{id: 1, age: 1},
{id: 2, age: 2}
]
}
}
addCount = () => {
this.setState({
count: this.state.count + 1,
}, () => {
console.log('在回調中獲取異步后的結果:', this.state.count)
})
console.log(this.state.count) // 這里是異步的
}
同步的setState
:
- 在
setTimeout
中是同步的
render() {
return (
<div>
<button onClick={this.addCount2}>setTimout同步累加</button>
</div>
)
}
addCount2 = () => {
setTimeout(() => {
this.setState({
count: this.state.count + 1
})
console.log('setTimeout中的setState:', this.state.count)
})
}
- 在自定義DOM事件中剃袍,是同步的
在componentDidMount
生命周期中增加自定義事件,發(fā)現(xiàn)執(zhí)行的setState
是同步的
注意:自定義事件要及時在componentWillUnmount
生命周期解綁捎谨,不然會造成內存泄露
class SetStateDemo extends Component{
constructor(props) {
super(props)
this.state = {
count: 0,
}
}
componentDidMount() {
// 自定義事件
document.body.addEventListener('click', this.addCount3)
}
addCount3 = () => {
this.setState({
count: this.state.count + 1
})
console.log('自定義事件中的setState:', this.state.count)
}
componentWillUnmount() {
// 自定義事件要及時解綁民效,否則會造成內存泄露
document.body.removeEventListener('click', this.addCount3)
}
render() {
return (
<div>
<p>{this.state.count}</p>
<button>累加</button>
</div>
)
}
setState是否會被合并
- 當使用對象來修改狀態(tài)時,會被合并
因為在merge
方法中涛救,this.setState
是異步執(zhí)行的畏邢,當執(zhí)行完this.setState({ count: this.state.count + 1 })
時,this.state.count還沒有更新检吆;
所以執(zhí)行下一個this.setState
時舒萎,this.state.count
還是原來的0
,所以執(zhí)行完三次后蹭沛,相當于執(zhí)行三次this.setState({ count: 1 })
臂寝;
當異步更新時,這三次結果被合并了摊灭,類似執(zhí)行Object.assign({count: 1}, {count: 1}, {count: 1})
咆贬,結果為{count: 1}
,所以點擊一次this.state.count
結果是每次加1
render() {
return (
<div>
<button onClick={this.merge}>使用對象來setState</button>
</div>
)
}
merge = () => {
// 使用對象時帚呼,結果會被合并
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
}
-
使用函數(shù)時掏缎,結果則不會被合并
使用函數(shù)時,會默認有兩個參數(shù):- preState: 為當前組件的
state
- props: 為父組件傳遞的屬性
通過
preState
修改state
萝挤,然后return
返回一個對象御毅,跟我們平常使用setState
的對象格式是一樣的 - preState: 為當前組件的
render() {
return (
<div>
<button onClick={this.merge}>使用函數(shù)來setState</button>
</div>
)
}
merge = () => {
// 使用函數(shù),則不會被合并怜珍,執(zhí)行結果+3
this.setState((preState, props) => {
return {
count: preState.count + 1
}
})
this.setState((preState, props) => {
return {
count: preState.count + 1
}
})
this.setState((preState, props) => {
return {
count: preState.count + 1
}
})
console.log('使用函數(shù):', this.state.count)
}
可以看到端蛆,無論是用對象,還是用函數(shù)酥泛,setState
是異步返回結果是不會變的