Comments (7)
this.setState((state) => {
console.log(state.val) // 0
return { val: state.val + 1 }
})
this.setState((state) => {
console.log(state.val) // 1
return { val: state.val + 1 }
})
上面的代码之所以可以说实现0,1 效果,其原理如下:
class Component {
constructor() {
this.updateQueue = [];
this.callbackQueue = [];
this.isBatchingUpdates = false;
this.state = {
val: 0
};
}
setState(partialState, callback) {
if(this.isBatchingUpdates) {
this.updateQueue.push(partialState)
this.callbackQueue.push(callback)
}
}
add() {
this.isBatchingUpdates = true
this.setState((preState) => ({val: preState.val + 1}), () => {
console.log(this.state.val)
})
this.setState((preState) => ({val: preState.val + 1}), () => {
console.log(this.state.val)
})
this.setState((preState) => ({val: preState.val + 1}), () => {
console.log(this.state.val)
})
this.flushQueue()
}
flushQueue() {
let partialState = this.updateQueue.reduce((pre, next) => {
return next(pre)
}, this.state)
this.state = {...this.state, ...partialState}
this.callbackQueue.map(callback => callback())
this.isBatchingUpdates = false;
}
}
let c = new Component()
c.add()
from react.
1. 保证内部的一致性
即使state是同步更新,props也不是。(你只有在父组件重新渲染时才能知道props)
2. 性能优化
将state的更新延缓到最后批量合并再去渲染对于应用的性能优化是有极大好处的,如果每次的状态改变都去重新渲染真实dom,那么它将带来巨大的性能消耗。
原理解释
面试官:“react中setState是同步的还是异步?”
我:“异步的,setState不能立马拿到结果。”面试官:“那什么场景下是异步的,可不可能是同步,什么场景下又是同步的?”
我:“......”setState并不是真正意义上的异步操作,它只是模拟了异步的行为
为什么这么说。可以通过下面的例子
不是真正意义上的异步操作class App extends Component { state = { count: 0 }; componentDidMount() { // 生命周期中调用 this.setState({ count: this.state.count + 1 }); console.log("lifecycle: " + this.state.count); setTimeout(() => { // setTimeout中调用 this.setState({ count: this.state.count + 1 }); console.log("setTimeout: " + this.state.count); }, 0); document.getElementById("div2").addEventListener("click", this.increment2); } increment = () => { // 合成事件中调用 this.setState({ count: this.state.count + 1 }); console.log("react event: " + this.state.count); }; increment2 = () => { // 原生事件中调用 this.setState({ count: this.state.count + 1 }); console.log("dom event: " + this.state.count); }; render() { return ( <div className="App"> <h2>couont: {this.state.count}</h2> <div id="div1" onClick={this.increment}> click me and count+1 </div> <div id="div2">click me and count+1</div> </div> ); } }探讨前,我们先简单了解下react的事件机制:react为了解决跨平台,兼容性问题,自己封装了一套事件机制,代理了原生的事件,像在jsx中常见的
onClick
、onChange
这些都是合成事件。那么以上4种方式调用setState(),后面紧接着去取最新的state,按之前讲的异步原理,应该是取不到的。然而,
setTimeout
中调用以及原生事件中调用的话,是可以立马获取到最新的state的。根本原因在于,setState并不是真正意义上的异步操作,它只是模拟了异步的行为。React中会去维护一个标识(isBatchingUpdates
),判断是直接更新还是先暂存state进队列。setTimeout
以及原生事件都会直接去更新state,因此可以立即得到最新state。而合成事件和React生命周期函数中,是受React控制的,其会将isBatchingUpdates
设置为 true,从而走的是类似异步的那一套。在 setTimeout 中去 setState 并不算是一个单独的场景,它是随着你外层去决定的,因为你可以在合成事件中 setTimeout ,可以在钩子函数中 setTimeout ,也可以在原生事件setTimeout,但是不管是哪个场景下,基于event loop的模型下, setTimeout 中里去 setState 总能拿到最新的state值。
总结
setState
只在合成事件和钩子函数中是“异步”的,在原生事件和setTimeout
中都是同步的。setState
的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形式了所谓的“异步”,当然可以通过第二个参数 setState(partialState, callback) 中的callback拿到更新后的结果。- setState 的
批量更新
优化也是建立在“异步”(合成事件、钩子函数)之上的,在原生事件和setTimeout
中不会批量更新,在“异步”中如果对同一个值进行多次 setState
, setState 的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同时 setState 多个不同的值,在更新时会对其进行合并批量更新。对于3 可以结合下面的例子:
class App extends React.Component { state = { val: 0 } componentDidMount() { this.setState({ val: this.state.val + 1 }) console.log(this.state.val) this.setState({ val: this.state.val + 1 }) console.log(this.state.val) setTimeout(_ => { this.setState({ val: this.state.val + 1 }) console.log(this.state.val); this.setState({ val: this.state.val + 1 }) console.log(this.state.val) }, 0) } render() { return <div>{this.state.val}</div> } }
结合上面分析的,钩子函数中的 setState 无法立马拿到更新后的值,所以前两次都是输出0,当执行到 setTimeout 里的时候,前面两个state的值已经被更新,由于 setState 批量更新的策略, this.state.val 只对最后一次的生效,为1,而在 setTimmout 中 setState 是可以同步拿到更新结果,所以 setTimeout 中的两次输出2,3,最终结果就为 0, 0, 2, 3 。
想一下,如何将上面的代码中前两次连续 + 1 都执行呢?
class App extends React.Component { state = { val: 0 } componentDidMount() { this.setState((state) => { console.log(this.state.val) // 0 return { val: this.state.val + 1 } }) this.setState((state) => { console.log(this.state.val) // 1 return { val: this.state.val + 1 } }) setTimeout(_ => { this.setState({ val: this.state.val + 1 }) console.log(this.state.val); // 3 this.setState({ val: this.state.val + 1 }) console.log(this.state.val) // 4 }, 0) } render() { return <div>{this.state.val}</div> } }
最后一段,运行结果还是 0 0 2 3,并不是 0 1 3 4
from react.
楼上说的对 , 运行结果还是 0 0 2 3,并不是 0 1 3 4, 楼主大佬解释下呗
from react.
改成这样就对了
componentDidMount() {
this.setState((state) => {
console.log(state.val) // 0
return { val: state.val + 1 }
})
this.setState((state) => {
console.log(state.val) // 1
return { val: state.val + 1 }
})
setTimeout(_ => {
this.setState({ val: this.state.val + 1 })
console.log(this.state.val); // 3
this.setState({ val: this.state.val + 1 })
console.log(this.state.val) // 4
}, 0)
}
from react.
改成这样就对了
componentDidMount() { this.setState((state) => { console.log(state.val) // 0 return { val: state.val + 1 } }) this.setState((state) => { console.log(state.val) // 1 return { val: state.val + 1 } }) setTimeout(_ => { this.setState({ val: this.state.val + 1 }) console.log(this.state.val); // 3 this.setState({ val: this.state.val + 1 }) console.log(this.state.val) // 4 }, 0) }
因为第二个setState里this.state的数据还不是最新的,此时this.state.val还是0。而参数state表示上一次更新后的state,是最新的,state.val是1。
可以参考这篇blog:https://blog.csdn.net/Mr_28/article/details/84778001
from react.
@DangoSky 优秀
from react.
componentDidMount() {
this.setState({ val: this.state.val + 1 })
console.log(this.state.val)
this.setState({ val: this.state.val + 1 })
console.log(this.state.val)
.....
}
对于上面一段代码为什么会是0,0 ,笔者将源码缩略后,原理如下:
class Component {
constructor() {
this.updateQueue = [];
this.isBatchingUpdates = false; // 是否需要暂存
this.state = {
val: 0
};
}
setState(newState) {
if(this.isBatchingUpdates) {
this.updateQueue.push(newState)
}
}
add() {
this.isBatchingUpdates = true
this.setState({number: this.state.val + 1}) // this.state.val = 0
this.setState({number: this.state.val + 1}) // this.state.val = 0
this.setState({number: this.state.val + 1}) // this.state.val = 0
this.flushQueue()
}
flushQueue() {
this.updateQueue.map(newState => this.state = newState)
this.isBatchingUpdates = false;
}
}
let c = new Component()
c.add()
from react.
Related Issues (20)
- 状态管理(this.state)篇 HOT 3
- a
- React Element 究竟是什么?
- reacte 的 Fiber 概念
- JSX
- React.Component 和函数组件
- React合成事件 —— SyntheticEvent
- react ref 篇
- 高阶组件 (HOC)
- CommonJS syntax and ES Module syntax to importReact
- React 和 TypeScript 结合
- React 上下文
- 好用相关第三方控件
- React 运行时优化方案的演进
- React之key详解
- react 生命周期图
- 使用闭包提升你的 React 性能 -- memoize-one
- 为什么要写 super(props)
- # react 16 生命周期的变化
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from react.