setState异步、同步、宏任务、微任务?
1、在react生命周期里,当执行setState时,setState会放入队列。
代码执行后,从表面看形成了异步,但不是异步,代码执行还是同步的。
handleClick = () => {
console.log('1')
this.setState({
count: 2
}, () => {
console.log('2')
})
console.log('3')
}
// 1,3,2
setState
看着像是一个异步的操作。
原因,在 React 的生命周期以及绑定的事件流中,所有的 setState
会优先缓存到一个队列中,在整个事件结束后或者 mount 流程结束后,会取出之前缓存的 setState
队列进行一次执行,触发 state 更新。
2、如果我们执行跳出 React 的事件流或者生命周期,就能打破 React 对 setState
的掌控。
最简单的方法,就是把 setState
放到 setTimeout
中。
handleClick = () => {
setTimeout(() => {
console.log('1')
this.setState({
count: 2
}, () => {
console.log('2')
})
console.log('3')
})
}
// 1,2,3
一、Arguments
1、arguments[0]
(1)对象
handle() {
this.setState({
count: 1 })
}
(2)函数
handle() {
this.setState((prevState) => ({
count: prevState.count + 1 }))
}
在setState中的第1个参数,可以传对象或函数
2、arguments[1]
handle() {
this.setState({ count: 1 }, () => {
console.log(this.state)
})
}
在setState中的第2个参数,是回调函数。因为setState()是异步执行,故可以通过第2个参数中的回调函数执行同步操作。
二、异步与同步
1、多个setState()的执行
handle() {
// 初始化 `count` 为 0
console.log(this.state.count) // -> 0
this.setState({ count: this.state.count + 1 })
this.setState({ count: this.state.count + 1 })
this.setState({ count: this.state.count + 1 })
console.log(this.state.count) // -> 0
}
初始count与执行完setState()后的count都是0,这是因为 setState 是异步执行。
如何解决:
handle() {
this.setState((prevState) => ({ count: prevState.count + 1 }))
this.setState((prevState) => ({ count: prevState.count + 1 }))
this.setState((prevState) => ({ count: prevState.count + 1 }))
}
// 或者使用async / await
当this.setState()被调用的时候,React会重新调用render方法来重新渲染UI。
如果每次执行调用就去进行重新渲染,会损耗性能。批量更新,可以避免短期内的多次渲染,变成一次性更新。
所以setState就相当于是一个异步操作,不能立即被修改。state的值在修改了之后并不会立即被修改,而是也有一个类似的队列,setState通过一个队列机制实现state的更新。
当执行setState时,会把需要更新的state合并后放入状态队列,而不会立刻更新this.state,利用这个队列机制可以高效的批量的更新state。
2、setState的执行步骤
(1)当调用setState时,实际上会执行enqueueSetState方法,并对partialState以及_pendingStateQueue更新队列进行合并,最终通过enqueueUpdate执行state更新。
performUpdateIfNecessary方法获取_pendingElement、_pendingStateQueue、_pendingForceUpdate,并调用reciveComponent和updateComponent方法进行组件更新。
(2) 如果组件当前正处于update事务中,则先将Component存入dirtyComponent中。否则调用batchedUpdates处理。
(3)batchedUpdates发起一次transaction.perform()事务。
开始执行事务初始化、运行、结束三个阶段:
初始化:事务初始化阶段没有注册方法,故无方法要执行
运行:执行setSate时传入的callback方法,一般不会传callback参数
结束:更新isBatchingUpdates为false,并执行FLUSH_BATCHED_UPDATES这个wrapper中的close方法
(4)FLUSH_BATCHED_UPDATES在close阶段,会循环遍历所有的dirtyComponents,调用updateComponent刷新组件,并执行它的pendingCallbacks, 也就是setState中设置的callback。