在react的类组件中,修改state的状态,是通过setState这函数来进行修改的。
错误的写法:
import React, { Component } from 'react'
export default class UnderStandSetState extends Component {
constructor() {
super()
this.state = {
message: 'james'
}
this.changeMessage = this.changeMessage.bind(this)
}
changeMessage() {
this.state.message = 'kobe'
console.log(this.state.message); //kobe
}
render() {
return (
<div>
<h1>{this.state.message}</h1>
<button onClick={this.changeMessage}>改变文本</button>
</div>
)
}
}
原因: 这样的写法,state的值虽然已经发生改变,但是react底层不知道state的值变了,所以页面上就不会更新。(这里跟vue的不一样,vue2有Object.defineProperty,vue3 的proxy对数据进行注册,监听)
正确的写法:
changeMessage() {
this.setState({
message: 'kobe'
})
}
通过setState函数来改变state的状态,页面上也会更新。
那么也会好奇?setState这个函数来自于哪里?
原因: class组件继承与react中的component,在component的原型上就有setState这个方法。
Component的部分源码
Component.prototype.setState = function(partialState, callback) {
invariant(
typeof partialState === 'object' ||
typeof partialState === 'function' ||
partialState == null,
'setState(...): takes an object of state variables to update or a ' +
'function which returns an object of state variables.',
);
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
从上面的源码,可以看出,setState是有两个参数,平时只用第一个参数,第二个参数为回调函数,拿取更新之后的值。在函数体内,第一个函数也分为对象形式
和函数形式
,所以这里setState也就有两种的使用方法。
setState的对象形式
changeMessage() {
this.setState({
message: 'kobe'
}, () => {
console.log(this.state.message) //kobe(最新的值)
})
}
注意点:
this.state = {
count: 1
}
btnAdd() {
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
}
render() {
return (
<h1>{this.state.count}</h1>
<button onClick={this.btnAdd}> + </button>
)
}
当点击+
的时候,看逻辑来说的话,应该会在原来的基础上+3
。但是呢?并没有。这里他会进行合并,如果是相同的key值的话。类似于Javascript中的Object.assign()的工作原理。
setState的函数形式
changeMessage() {
this.setState((prevState, props) => {
return {
message: 'kobe'
}
}, () => {
console.log(this.state.message) //kobe(最新的值)
})
}
//参数1: 当前的state的状态
//参数2: 传递过来的props
注意点:
btnAdd() {
this.setState((prev) => {
return {
count: prev.count + 1
}
})
this.setState((prev) => {
return {
count: prev.count + 1
}
})
this.setState((prev) => {
return {
count: prev.count + 1
}
})
}
这里就会跟我想的一样的执行,从1
变为4
setState的函数形式,更加适用于处理复杂的逻辑处理。
setState函数同步?异步?
在使用setState的函数中,我们改变了值,下一步取值的话,往往是最开始的值,而不是最新的值(当然我们可以在setState的第二个参数拿到最新的值),造成这种原因是为什么呢?
setState通过一个队列机制实现state的更新。当执行setState时,会把需要更新的state合并后放入状态队列,而不会立刻更新this.state,利用这个队列机制可以高效的批量的更新state。
这样更加有利于性能优化。
一般情况下,在没有进行特殊处理下,setState是异步函数。
但是在一些情况下,它又是同步函数
情况一:
在定时器中,是同步函数
changeMessage() {
setTimeout(() => {
this.setState({
message: 'kobe'
})
console.log(this.state.message) //kobe
})
}
情况二:
在一些原生JS的API中,也是同步函数
btn.addEventListener('click', () => {
this.setState({
message: 'kobe'
})
console.log(this.state.message) //kobe
})
})
总结:
- 在组件生命周期中或React合成事件中,setState是异步的
- 在setTimeout或者原生dom中,setState是同步的