setState()批处理

简介: setState()批处理

react开发对setState的使用可能一点也不陌生,但肯定会碰到过这种情况:

import React from 'react'
export default class BatchedDemo extends React.Component {
state = {

number: 0,
AI 代码解读

}

handleClick = () => {

this.countNumber()
}
AI 代码解读

countNumber() {

this.setState({
  number: this.state.number + 1
})

this.setState({
  number: this.state.number + 1
})

this.setState({
  number: this.state.number + 1
})
AI 代码解读

}

render() {

return <button id="myButton" onClick={this.handleClick}>Num: {this.state.number}</button>
AI 代码解读

}
}
复制代码
å¨è¿éæå¥å¾çæè¿°

点击button按钮后,发现只加了1,why?

这就涉及到了setState的更新策略

setState 批量更新
除了virtual-dom的优化,减少数据更新的频率是另外一种手段,也就是React的批量更新。

顾名思义,批量更新,可以避免短期内的多次渲染,攒为一次性更新。

setState()合并策略:
我对攒为一次性更新的理解是:是覆盖而不是叠加(类似于Object.assign())

证明如下: 把countNumber函数改为如下,如果是覆盖,那么只会执行

number: this.state.number + 5,相当于把前面同类都覆盖了
countNumber() {

this.setState({
  number: this.state.number + 11
})

this.setState({
  number: this.state.number + 20
})

this.setState({
  number: this.state.number + 5,
})
AI 代码解读

}
复制代码
点击按钮后:

å¨è¿éæå¥å¾çæè¿°

所以如下操作:

this.setState({
age: 18
})
this.setState({
color: 'black‘
})
this.setState({
age: 20
})
this.setState({
name: 'yank'
})
复制代码
会被React合成为一次setState调用

this.setState({

name: 'yank',
age: 20, 
color: 'black'
AI 代码解读

})
复制代码
而我们要搞清楚的就是setState()到底是如何去合并的,我能自由控制它的合并吗?

setState()合并原理:
通过伪代码更好的去理解setState()是如何去合并的

setState实现

setState(newState) {
if (this.canMerge) {

this.updateQueue.push(newState)
return 
AI 代码解读

}

// 下面是真正的更新: dom-diff, lifeCycle...
...
}
复制代码
然后countNumber()方法调用之后,把隐式操作通过伪代码显示出来:

countNumber() {

    this.canMerge = true

this.setState({
  number: this.state.number + 11
})

this.setState({
  number: this.state.number + 20
})

this.setState({
  number: this.state.number + 5,
})
 
     this.canMerge = false

     // 通过this.updateQueue合并出finalState
  const finalState = ...  
  // 此时canMerge 已经为false 故而走入时机更新逻辑
  this.setState(finaleState) 
AI 代码解读

}
复制代码

可以看出 setState首先会判断是否可以合并,如果可以合并this.canMerge = true

,就直接返回了。直到this.canMerge = false时,代表finalState已经合并完成,就开始走更新,需要注意的是这些都是react内部的隐式操作,是发生在React内部的,React对它们有完全的控制权。

canMerge逻辑存在于哪里?
除了事件处理函数会执行canMerge逻辑,在执行componentDidMount前后也会有canMerge逻辑,可以理解为:React委托代理了所有的事件,在执行你的函数/componentDidMount之前,会执行React逻辑,这样React也是有时机执行canMerge逻辑的。

如何控制canMerge逻辑

批量更新是极好滴!我们当然希望任何setState都可以被批量,关键点在于React是否有时机执行canMerge逻辑,也就是React对目标函数有没有控制权。如果没有控制权,那么就不会执行canMerge逻辑,也就不会发生setState()被react隐式合并了

通过setTimeout脱离react的控制

import React from 'react'

export default class BatchedDemo extends React.Component {
state = {

number: 0,
AI 代码解读

}

handleClick = () => {

this.setState({
  number: this.state.number + 1
})
this.setState({
  number: this.state.number + 2
})
this.setState({
  number: this.state.number + 3
})

setTimeout(() => {
  this.setState({
    number: this.state.number + 4
  })
  this.setState({
    number: this.state.number + 5
  })
  this.setState({
    number: this.state.number + 6
  })
})
AI 代码解读

}
render() {

return <button id="myButton" onClick={this.handleClick}>Num:
{this.state.number}
</button>
AI 代码解读

}
}
复制代码
分析上述代码:

handleClick 是事件回调,React有时机执行canMerge逻辑,所以x为+1,+2,+3是合并的,handleClick结束之后canMerge被重新设置为false。注意这里有一个setTimeout(fn, 0)。 这个fn会在handleClick之后调用,而React对setTimeout并没有控制权,React无法在setTimeout前后执行canMerge逻辑,所以x为4,5,6是无法合并的,所以fn这里会存在3次dom-diff。React没有控制权的情况有很多: Promise.then(fn), fetch回调,xhr网络回调等等。

所以点击按钮: 3+4+5+6=18

å¨è¿éæå¥å¾çæè¿°

通过unstable_batchedUpdates重回react的控制

以上代码的setTimeout中,我想让react去拿回控制权,合并代码,怎么办呢?

需要用unstable_batchedUpdates这个API

代码如下:

import React from 'react'
import { unstable_batchedUpdates as batchedUpdates } from 'react-dom'

export default class BatchedDemo extends React.Component {
state = {

number: 0,
AI 代码解读

}

handleClick = () => {

this.setState({
  number: this.state.number + 1
})
this.setState({
  number: this.state.number + 2
})
this.setState({
  number: this.state.number + 3
})

setTimeout(() => {
  //通过这个api,让react拿回控制权,执行canMerge逻辑
  batchedUpdates(() => {
    this.setState({
      number: this.state.number + 4
    })
    this.setState({
      number: this.state.number + 5
    })
    this.setState({
      number: this.state.number + 6
    })
  })

})
AI 代码解读

}

render() {

return <button id="myButton" onClick={this.handleClick}>Num:
{this.state.number}
</button>
AI 代码解读

}
}
复制代码
打印如下:3+6=9

å¨è¿éæå¥å¾çæè¿°

最后看一下这个api的伪代码:

function unstable_batchedUpdates(fn) {
this.canMerge = true

fn()

this.canMerge = false
const finalState = ... //通过this.updateQueue合并出finalState
this.setState(finaleState)
}
复制代码
此篇文章转载于:https://blog.csdn.net/fesfsefgs/article/details/108023095

作者: Bill 本文地址: http://biaoblog.cn/info?id=1625016158478

版权声明: 本文为原创文章,版权归 biaoblog 个人博客 所有,欢迎分享本文,转载请保留出处,谢谢!

目录
打赏
0
0
0
0
0
分享
相关文章
别再只用普通函数了!箭头函数的四大神奇区别,让你的代码飞起来!
【8月更文挑战第23天】在Web前端开发中,JavaScript的箭头函数(引入于ES6)提供了一种比传统函数更加简洁的定义方法。箭头函数使用 &quot;=&gt;&quot; 替代 &quot;function&quot; 关键字,并且自动绑定外部 &quot;this&quot; 上下文,避免了传统函数中 &quot;this&quot; 值因调用方式不同而变化的问题。此外,箭头函数不拥有自己的 &quot;arguments&quot; 对象,但可以通过剩余参数语法获取所有参数。需要注意的是,箭头函数不能作为构造函数使用。理解这些差异有助于开发者编写更高效、清晰的代码。
363 0
优化前端性能的十大最佳实践
在现代网页开发中,前端性能优化不仅仅是为了提升用户体验,还能显著提高网站的加载速度和响应时间。本文探讨了十大最佳实践,从优化资源加载到减少网络请求,再到提高页面渲染效率,每个实践都旨在解决常见的性能瓶颈。通过实现这些策略,开发者可以显著提升前端性能,提升用户满意度,并确保网站在各种设备上的流畅运行。
怎么区分5G卡片开启的网络类型是NSA(非独立组网)还是SA(独立组网)
要确定5G卡片开启的网络类型是NSA(非独立组网)还是SA(独立组网),你通常需要进行以下操作:
Vue 3 Composition API 与 Options API:全面比较两者的区别和优缺点
Vue 3 Composition API 与 Options API:全面比较两者的区别和优缺点
【亮剑】如何在Java项目中结合Spring框架实现邮件发送功能
【4月更文挑战第30天】本文介绍了如何在Java项目中结合Spring框架实现邮件发送功能。首先,需在`pom.xml`添加Spring和JavaMail依赖。然后,在`applicationContext.xml`配置邮件发送器,包括SMTP服务器信息。接着,创建一个使用依赖注入的`EmailService`类,通过`JavaMailSender`发送邮件。最后,调用`EmailService`的`sendSimpleEmail`方法即可发送邮件。最佳实践包括:使用配置管理敏感信息,利用`MimeMessage`构造复杂邮件,异常处理和日志记录,以及在大量发送时考虑使用邮件队列。
185 1
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问