当前位置: 首页 > Web前端 > JavaScript

深入浅出setState的原理

时间:2023-03-27 14:22:00 JavaScript

前言我自己的想法(2021)8月份面试的时候,被面试官问了几个setState的问题。现在想来,虽然回答了上面的问题,但是理解的并不深刻。我知道setState是为了性能而设计成“异步”的,但是当涉及到源代码解释时,我很茫然;我知道如何让它同步,但是当涉及到真正的代码情况时,我不知道如何下手。毕竟我是准备把这些概念当面写下来的,并没有真正理解。在了解setState之前,我们问了几个常见的问题。setState是同步的还是异步的?如果是异步的,如何让它同步呢?为什么要这样设计?使用React的一个基本概念和概念是UI=f(data)。修改数据带动UI变化,那么怎么修改呢?React提供了一个API——setState(类组件的修改方法)官网介绍:setState()将组件状态的更新加入队列,并通知React组件及其子组件需要用更新后的状态重新渲染。这是更新UI以响应事件处理程序和处理服务器数据的主要方式。为了获得更好的感知性能,React延迟调用它,然后一次性更新多个组件。React不保证状态更改会立即生效setState()并不总是立即更新组件。它分批推迟更新。这使得在调用setState()之后立即读取this.state成为一种危险。为了消除隐患,请使用componentDidUpdate或者setState的回调函数(setState(updater,callback)),两者都可以保证应用更新后触发,除非shouldComponentUpdate()返回false,否则setState()会一直执行重新-渲染操作。如果使用了可变对象,并且在shouldComponentUpdate()中无法实现条件渲染,那么只在新旧状态不一致时才调用setState()可以避免不必要的重新渲染使用方法setState(updater,[callback])参数一是带形式参数的更新函数:(state,props)=>stateChange//例如//this.setState((state,props)=>{//return{counter:state.counter+props.step};//});setState的第一个参数除了可以接受函数,还可以接受对象类型:setState(stateChange[,callback])//例如:this.setState({count:2})setState的第二个参数是可选的回调函数将在setState完成合并并重新渲染组件后执行。通常,我们推荐使用componentDidUpdate而不是这个方法setState(stateChange[,callback])//例如:this.setState({count:2},()=>{console.log(this.state.count)})使用componentDidUpdate相对于setState回调有什么优势?stackoverflow上有人问过,也有人回答过:setState什么时候才能更好的实现一致的逻辑批量更新?当外部代码需要等待状态更新时,比如PromisesetState的特性——批处理如果在同一个周期处理多个setState,比如同一个周期设置多次商品数据,则相当于:this.setState({count:state.count+1});this.setState({count:state.count+1});this.setState({count:state.count+1});//===Object.assign(count,{quantity:state.quantity+1},{quantity:state.quantity+1},...)后调用的setState会覆盖同一个循环中先调用的setState的值setState(stateChange[,callback])setState((state,props)=>stateChange[,callback])setState一定会触发update过程,但不一定会导致render被执行,因为shouldCompomentUpdate可以返回false批处理导致的问题问题一:为什么使用setStatecontinuously=0时不能实时更改state.count;this.setState({count:state.count+1});this.setState({count:state.count+1});this.setState({count:state.count+1});//state.count===1,因为this.setState而不是3该方法是批处理的,后面调用的setState会在同一个周期覆盖先调用的setState的值,如下图:state.count=0;this.setState({count:state.count+2});这个.setState({count:state.count+3});this.setState({count:state.count+4});//state.count===4问题2:为什么setState而不是this。state.xx=哦?因为setState所做的不仅仅是修改this.state的值,最重要的是它会触发React的更新机制,会进行diff,然后将patch部分更新到真正的dom中。如果你直接this.state.xx=oo的话,state的值确实会改变,但是不会驱动React重新渲染。setState可以帮助我们更新视图,触发shouldComponentUpdate、render等一系列函数调用。对于批处理,React会将setState的效果放入队列中,在事件结束后生成重新渲染,以尽量减少VirtualDOM和DOM树操作,提高性能。调用setState后,ReactPeriodic函数的生命周期会依次执行staticgetDerivedStateFromPropsshouldComponentUpdaterendergetSnapshotBeforeUpdatecomponentDidUpdate。问题三:那为什么会出现异步呢?(为什么要这样设计?)因为性能优化。如果每次设置setState都需要更新数据,那么更新过程会经历5个生命周期。一轮生命周期结束后,需要花费大量的时间来比较render函数的结果和更新真实的DOM。因此,将每次调用放在一起一次性处理,可以减少对DOM的操作,提高应用性能。问题四:如何在异步函数中准确获取更新后的状态?通过第二个参数中的回调获取更新结果setState(partialState,callback)onHandleClick(){this.setState({count:this.state.count+1,},()=>{console.log("Callbackafterclick",this.state.count);//最新值});}或者你可以直接给state传一个函数来显示同步情况this.setState(state=>{console.log("functionmode",state.count);返回{count:state.count+1};});执行原理首先了解三种渲染模式:legacy模式:ReactDOM.render(,rootNode)。这是React应用程序当前使用的方式。目前没有移除此模式的计划,但此模式可能不支持新的阻塞模式:ReactDOM.createBlockingRoot(rootNode).render()。目前是实验性的,作为迁移到并发模式的第一步:ReactDOM.createRoot(rootNode).render()。目前正在实验中,计划在未来稳定后作为React的一种模式开发模式。此模式启用具有不同优先级的所有新功能,并且可以中断更新过程。在legacy模式下,在React的setState函数的实现中,会根据一个变量isBatchingUpdates来判断是直接更新this.state还是放入队列中,后面再说,而isBatchingUpdates默认为false,也就是说setState将同步更新this.state。但是有一个函数batchedUpdates,它会把isBatchingUpdates变为true,当React在调用事件处理函数之前调用这个batchedUpdates的后果就是React控制的事件处理进程setState不会像native那样同步更新this.stateaddEventListener绑定的事件,setTimeout/setInterval会同步走,另外就是React控制的事件处理setState会是异步的,并发方式是异步的,这也是以后React18默认的方式。小结首先,我们总结一下关键知识点setState不会立即改变React组件中state的值。setState触发一个组件更新过程,触发多次重绘setState函数调用的效果会被合并(批处理)。其次,回答文章开头的问题(第二个和第三个问题在文章中已经回答)setState是同步的还是异步的?代码同步,渲染查看模式legacy模式,非原生事件情况下异步,setTimeout/setInterval;addEventListener绑定native事件时会同步,setTimeout/setInterval并发方式:异步参考setState:这个API是怎么设计的?setState为什么不会同步更新组件状态setState什么时候同步更新状态浅入深退出setState(上)浅入深退出setState(下)重新认识React的setState你真的了解setState吗?setState是同步的还是异步的?React中的setState是宏任务还是微任务?使用com有什么好处ponentDidUpdate超过setState回调?深度学习:何时以及为何对setState()进行批处理?深入挖掘:为什么不更新this.state?