这次把React中的异步更新状态搞明白

为什么说React中状态更新可能是异步的?

在React中,状态更新被描述为可能是异步的,主要是指在多个setState调用的情况下,React可能会延迟状态更新或将它们合并为单个更新,来优化性能和响应速度。这一机制是React用来提升其效率和响应能力的。

举个例子:

react 复制代码
const [count, setCount] = useState(0)

useEffect(() => { 
    setCount(count + 1) 
    setCount(count + 1) 
    setCount(count + 1) 
}, [])

在这段的代码中,count的值只会增加 1 ,useEffect仅在组件挂载时运行一次,即为一个渲染周期 。由于每一次调用setCount(count + 1)都是基于相同的初始count值来计算的,所以只有一次有效的增加。原因在于React的状态更新(特别是使用同一个状态更新函数时,如setCount)是异步且合并的。这意味着在一个渲染周期内,所有对同一状态的更新会被合并并计算为一个更新。

那么就可以认为,这里的状态更新与setState并没有关系,而是与state有关系。因为在一个渲染周期内,state的值是相同的。在同一个渲染周期(或者说在当前的函数调用事件处理函数 、或是生命周期方法 的执行过程中)里,不论你调用了多少次setState,所有这些setState都是基于同一个state的原始值进行计算的。

如果你连续多次直接以state的当前值去计算下一个state(比如setState(count + 1)),实际上每次计算得到的值都是基于同一个count值,因此这些更新最终会被合并,只有一次效果会被应用。

那么在同一个渲染周期中,如何同步更新state值:

要解决这个问题,确保每次setState都基于最新的state值,需要使用函数式的setState更新(即setState(prevState => prevState + 1))。这种方式每次都会接收到最新的state值作为参数,无论之前进行了多少次更新,每次函数都会基于最近更新后的state值计算新的值:

react 复制代码
const [count, setCount] = useState(0)

useEffect(() => { 
    setCount(pre => pre + 1) 
    setCount(pre => pre + 1) 
    setCount(pre => pre + 1) 
}, [])

在这种情况下,每次更新都是基于状态的最新值,因此count会增加3。

因此,在一个渲染周期内state的值是不变的这一事实,而非setState本身的机制,导致了只有基于最初的state值的一次有效计算。这解释了为何在同一个渲染周期中,基于state的直接更新可能不会如预期那样累加更新。

为什么在一个渲染周期中,不论同步执行多少次state,的值都是相同的呢?

因为在调用setState方法更新状态时,React会将这个更新操作排入一个队列中,并不会立即执行这个操作。只有当当前的执行栈清空,React才会从队列中取出所有的更新操作,并开始重新渲染组件。在这个过程中,React会尝试合并多个状态更新,以减少不必要的渲染次数。这就意味着,在一个渲染周期中触发的所有状态更新,实际上只会引起一次重新渲染,而在这次重新渲染发生之前,状态的值都不会发生变化。

这一机制是React用来提升其效率和响应能力的,React为什么这样做呢?

  1. 性能优化 :React通过批处理状态更新来减少不必要的渲染次数。如果每次调用setState都会立即触发组件的重新渲染,那在处理大量状态更新时,应用的性能会受到严重影响。通过将多个更新合并成单个更新,React可以减少渲染次数,从而提高性能。

  2. 一致性保障:在事件处理、生命周期方法或钩子(Hooks)中,React保证了状态的一致性。在这些情况下,React会延迟状态更新的应用,直到所有处理函数执行完毕。这确保了在任何给定时刻,组件的状态都是一致的,并且避免了在短时间内由于状态不一致而产生的潜在错误。

  3. 避免不必要的计算和渲染:通过异步状态更新和批处理,React能够合并多次状态更新,避免了因状态的微小变化而导致的整个组件树的不必要重新计算和渲染。这意味着只有当所有状态更新完成后,React才计算最终状态,并基于此状态重新渲染UI,从而提高了应用的效率。

要注意的是,尽管React中的状态更新可能是异步的,但这并不意味着它们是在JavaScript事件循环的下一个tick中执行的。这里的"异步"是相对于代码的执行流程而言的,意味着React可能会暂时延迟更新操作,而不是立即执行。

相关推荐
Irene19912 小时前
ElementPlus 与成熟后台框架对比:vue-element-plus-admin、vue-pure-admin等
前端·ui·框架·vue3
尘中客6 小时前
放弃 Echarts?前端直接渲染后端高精度 SVG 矢量图流的踩坑记录
前端·javascript·echarts·前端开发·svg矢量图·echarts避坑
FreeBuf_6 小时前
Chrome 0Day漏洞遭野外利用
前端·chrome
小彭努力中6 小时前
199.Vue3 + OpenLayers 实现:点击 / 拖动地图播放音频
前端·vue.js·音视频·openlayers·animate
2501_916007476 小时前
网站爬虫原理,基于浏览器点击行为还原可接口请求
前端·javascript·爬虫·ios·小程序·uni-app·iphone
前端大波7 小时前
Sentry 每日错误巡检自动化:设计思路与上手实战
前端·自动化·sentry
ZC跨境爬虫8 小时前
使用Claude Code开发校园交友平台前端UI全记录(含架构、坑点、登录逻辑及算法)
前端·ui·架构
慧一居士8 小时前
Vue项目中,何时使用布局、子组件嵌套、插槽 对应的使用场景,和完整的使用示例
前端·vue.js
Можно8 小时前
uni.request 和 axios 的区别?前端请求库全面对比
前端·uni-app
M ? A9 小时前
解决 VuReact 中 ESLint 规则冲突的完整指南
前端·react.js·前端框架