函数法
this.setState(state => ({
roles: [...state.roles,role]
}))
能保证每次接收到的state最新,支持同一个函数多次setState同一个变量
对象法
this.setState({
roles: [...this.state.roles,role]
})
多次setState同一个变量,不会产生多次效果,(只能产生一次效果)
总结:一般来说,当我们更新的变量与原变量无关时,使用对象法,否则使用函数法
setState是同步还是异步
- 在react控制的回调函数中是异步的,例如
生命周期钩子/react监听事件
//生命周期钩子
componentDidMount() {
}
//react监听事件
getList = () => {
}
- 在非react控制的回调函数中是同步的,例如
定时器回调/ 原生事件监听/promise回调
//定时器回调
setTimeout(() => {},1000)
// 原生事件监听
btn.onclick = function() {}
//promise回调
new Promise((resolve,reject) => {})
解决setState的异步问题
//可以通过setState的第二个参数(回调函数),拿到最新的值
getList = () => {
this.setState({
role
}, () => {
console.log('最新的role',role);
})
}
setState的render问题
如果setState传递的是一个引用类型,只改变对象(数组)中的某个值,不会render
//只改变对象中的某个属性,不能够更新成功
const m1 = this.state.m1;
m1.count = 2;
this.setState({m1})
正确做法
this.setState(state => ({
m1:{count:state.m1.count+1}
或者
const m1 = this.state.m1;
m1.count = 2;
m1: {...m1}
}))
关于setState的面试题
<div id="example"></div>
class StateTest extends React.Component {
state = {
count: 0,
}
componentDidMount() {
this.setState({ count: this.state.count + 1 })
this.setState({ count: this.state.count + 1 })
console.log(this.state.count) // 2 ==> 0
this.setState(state => ({ count: state.count + 1 }))
this.setState(state => ({ count: state.count + 1 }))
console.log(this.state.count) // 3 ==> 0
setTimeout(() => {
this.setState({ count: this.state.count + 1 })
console.log('timeout', this.state.count) // 10 ==> 6
this.setState({ count: this.state.count + 1 })
console.log('timeout', this.state.count) // 12 ==> 7
}, 0)
Promise.resolve().then(value => {
this.setState({ count: this.state.count + 1 })
console.log('promise', this.state.count) // 6 ==>4
this.setState({ count: this.state.count + 1 })
console.log('promise', this.state.count) // 8 ==> 5
})
}
render() {
const count = this.state.count
console.log('render', count) // 1 ==> 0 4 ==>3 5 ==>4 7 ==>5 9 ==>6 11 ==>7
return (
<div>
<p>{count}</p>
</div>
)
}
}
前一个数字代表顺序,后一个数字代表打印的结果
1 ==> 0 程序第一次进来,首先render,读取state的值为0
2 ==> 0 由于在钩子函数中,setState是异步函数,(promise和setTimeout回调函数中是同步的)所以先打印,结果还是0
3 ==> 0 原因与2相同
4 ==>3 由于setState和promise是微任务,setTimeout是宏任务,先执行宏任务,在执行微任务,所以先执行setState =》promise(按顺序)=》setTimeout
经过4次setState,但是this.setState({ count: this.state.count + 1 })这种方式,更新变量效果会合并,相当于只执行一次(异步setState,执行完所有的setState,才会render,而同步的setState,每次都会触发render)
5 ==> 4 执行promise中的setState导致render,
6==>4 promise中打印count
7==> 5 因为是同步的所以结果不会合并
8==>5 之后但原因都和之前的相同,这里就不雷同了