摘要:根本原因在于,并不是真正意义上的异步操作,它只是模拟了异步的行为。而合成事件和生命周期函数中,是受控制的,其会将设置为,从而走的是类似异步的那一套。总结此处总结是直接引用了只在合成事件和钩子函数中是异步的,在原生事件和中都是同步的。
如何使用setState
在 React 日常的使用中,一个很重要的点就是,不要直接去修改 state。例如:this.state.count = 1是无法触发 React 去更新视图的。因为React的机制规定,一个state的更新,首先需要调用 setState 方法。
this.setState({ count: 1 })
这样便能触发重新渲染。稍有经验的开发者会知道,setState 方法其实是 “异步” 的。即立马执行之后,是无法直接获取到最新的 state 的,需要经过 React 对 state 的所有改变进行合并处理之后,才会去计算新的虚拟dom,再根据最新的虚拟dom去重新渲染真实dom。
class App extends Component { state = { count: 0 } componentDidMount(){ this.setState({count: this.state.count + 1}) console.log(this.state.count) // 0 } render(){ ... } }
demo请点击
那怎么才能获取到修改后的state呢?React为我们提供了一个回调去实现。
... this.setState({count: this.state.count + 1}, ()=>{ console.log(this.state.count) // 1 }) ...
回调里的 state 便是最新的了,原因是该回调的执行时机在于state合并处理之后。如果我们这样去做:
... this.setState({count: this.state.count + 1}) this.setState({count: this.state.count + 1}) ...
实际最终的 count 会等于 1,原因是执行时得到的 this.state.count = 0。那怎么实现结果为 2 呢?
... this.setState(prevState => {count: prevState.count + 1}); this.setState(prevState => {count: prevState.count + 1}); ...
setState()实际上可以接受一个函数作为参数,函数的首个参数就是上一次的state。
以上介绍了setState的三种使用方式,下面我们来看看它们的执行时机是怎样的:
... this.setState({ count: this.state.count + 1 }); console.log("console: " + this.state.count); // 0 this.setState({ count: this.state.count + 1 }, () => { console.log("console from callback: " + this.state.count); // 2 }); this.setState(prevState => { console.log("console from func: " + prevState.count); // 1 return { count: prevState.count + 1 }; }, ()=>{ console.log("last console: "+ this.state.count) }); ...
执行结果:
console: 0 console from func: 1 console from callback: 2 last console: 2
React 其实会维护着一个 state 的更新队列,每次调用 setState 都会先把当前修改的 state 推进这个队列,在最后,React 会对这个队列进行合并处理,然后去执行回调。根据最终的合并结果再去走下面的流程(更新虚拟dom,触发渲染)。
setState为什么要设计成异步的因为setState()之后无法立马获取最新的 state,给人的感觉便是异步去设置状态。也确实是有异步的感觉(实 际原理后面讲诉)。那么为什么 React 要把状态的更新设计成这种方式呢?直接 this.state.count = 1 不好吗?
有兴趣的可以点击看看:https://github.com/facebook/react/issues/11527#issuecomment-360199710
这边简单总结下:
保证内部的一致性:即使state是同步更新,props也不是。(你只有在父组件重新渲染时才能知道props)
将state的更新延缓到最后批量合并再去渲染对于应用的性能优化是有极大好处的,如果每次的状态改变都去重新渲染真实dom,那么它将带来巨大的性能消耗。
setState真的是异步吗我们先来看一段代码,执行前建议大家先预估下结果:
demo请点击
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 (); } }couont: {this.state.count}
click me and count+1click me and count+1
探讨前,我们先简单了解下react的事件机制:react为了解决跨平台,兼容性问题,自己封装了一套事件机制,代理了原生的事件,像在jsx中常见的onClick、onChange这些都是合成事件。
那么以上4种方式调用setState(),后面紧接着去取最新的state,按之前讲的异步原理,应该是取不到的。然而,setTimeout中调用以及原生事件中调用的话,是可以立马获取到最新的state的。根本原因在于,setState并不是真正意义上的异步操作,它只是模拟了异步的行为。React中会去维护一个标识(isBatchingUpdates),判断是直接更新还是先暂存state进队列。setTimeout以及原生事件都会直接去更新state,因此可以立即得到最新state。而合成事件和React生命周期函数中,是受React控制的,其会将isBatchingUpdates设置为 true,从而走的是类似异步的那一套。
总结此处总结是直接引用了:https://juejin.im/post/5b45c57c51882519790c7441#heading-7
setState 只在合成事件和钩子函数中是“异步”的,在原生事件和 setTimeout 中都是同步的。
setState的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形式了所谓的“异步”,当然可以通过第二个参数 setState(partialState, callback) 中的callback拿到更新后的结果。
setState 的批量更新优化也是建立在“异步”(合成事件、钩子函数)之上的,在原生事件和setTimeout 中不会批量更新,在“异步”中如果对同一个值进行多次 setState , setState 的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同时 setState 多个不同的值,在更新时会对其进行合并批量更新。
参考:
你真的理解setState吗?
浅出深入setState(上)
浅出深入setState(下)
坦白说,自己的水平还是挺有限的,还需要努力努力再努力。只有懂了很多,才能写出有意义的东西吧。现在写的,大多都是根据别人的文章,自己理解了之后,换成自己的语言再表达一遍而已。。
慢慢来,总有一天可以突破这个界限的,真正写出属于自己的东西。
另外,真心感谢这些文章的作者给予的帮助。
关于我微信号:rcgrcg,欢迎交友~
为了生计,我也接外包项目的哦~
网站搭建(PC、H5、前后端全包,我们有团队的哦),APP开发(安卓和IOS),都是有成功案例的哦。
有兴趣的请联系我!!服务包您满意的那种!!
Good luck!
2018-11-18 厦门
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/108682.html
摘要:的批量更新优化也是建立在异步合成事件钩子函数之上的,在原生事件和中不会批量更新,在异步中如果对同一个值进行多次,的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同时多个不同的值,在更新时会对其进行合并批量更新。 api解析: setState(updater, [callback]) updater: 更新数据 FUNCTION/OBJECT callback: 更新成功后的回...
摘要:因为是深入系列文章,本文不会仔细介绍每个生命周期方法的使用,而是会重点讲解在使用组件生命周期时,经常遇到的疑问和错误使用方式。父组件发生更新导致的组件更新,生命周期方法的调用情况同上所述。 文:徐超,《React进阶之路》作者授权发布,转载请注明作者及出处 React 深入系列4:组件的生命周期 React 深入系列,深入讲解了React中的重点概念、特性和模式等,旨在帮助大家加深...
摘要:因为是深入系列文章,本文不会仔细介绍每个生命周期方法的使用,而是会重点讲解在使用组件生命周期时,经常遇到的疑问和错误使用方式。父组件发生更新导致的组件更新,生命周期方法的调用情况同上所述。 React 深入系列,深入讲解了React中的重点概念、特性和模式等,旨在帮助大家加深对React的理解,以及在项目中更加灵活地使用React。 组件是构建React应用的基本单位,组件需要具备数据...
摘要:同步渲染的痛点当应用的组件树特别庞大时,由于是单线程的,重新渲染一旦开始,中间不会停,如果这时候用户去操作,比如输入,点击按钮,此时页面是没有响应的。 React生命周期 基础生命周期钩子 constructor 如果你不初始化状态,也不绑定方法,那么你就不需要为React组件实现构造函数。在这里初始化状态可以直接对this.state赋值,在这里使用props时,应当通过this.p...
摘要:而在第二个参数中我们输出了改变后的即第五行输出,表明我们的更改生效了。而在的回调内,我们还调用了一个定义于内的事件函数,但是该事件函数内的也是同步的形式。 在react中,setState是用以改变class组件状态的函数,它有两种用法:一 传入一个updater函数,该函数有两个参数,一个是当前的state,还有一个是当前的props。该函数的返回值需要是一个更改的state值的对象...
阅读 3615·2021-11-24 09:39
阅读 2546·2021-11-15 11:37
阅读 2210·2021-11-11 16:55
阅读 5154·2021-10-14 09:43
阅读 3702·2021-10-08 10:05
阅读 3005·2021-09-13 10:26
阅读 2326·2021-09-08 09:35
阅读 3534·2019-08-30 15:55