关于在异步操作中访问React事件对象的小问题
最近撸React的代码时踩了个关于事件处理的坑,场景如下:在监听某个元素上会频繁触发的事件时,我们往往会对该事件的回调函数进行防抖的处理;防抖的包装函数大致长这样:
debounce = (fn, delay) => {
let timer: any = null;
return function(...args) {
if(timer) {
clearTimeout(timer);
}
timer = setTimeout(fn, delay, ...args);
}
}
核心部分就是用setTimeout()做延时执行,而问题就是出在这里。先说下结论,在React中如果要在异步操作中访问事件对象,则需要先在该事件对象上执行event.persist()。否则的话,在异步操作中访问事件对象时你会发现这个对象上大部分属性都是无效的了。
之前在项目中其他地方也见过这个方法也查了下知道这个东西,不过当时也只是知道有这么个方法并不太理解这么个方法存在的意义,现在好了,踩坑了吧,只好专门去了解下其中的缘由(=_=) 。 异步访问事件对象时其属性失效的原因在于事件派发并处理完后 这个对象不会马上被释放,而是将这个事件对象上的一些属性释放再回收放进被称为“事件池”的这么个地方。 看下react-dom中的这段源码:

在上面步骤中,派发完事件后,会判断事件对象event.isPersistent() 即是否有被持久化;而如果我没有在处理函数中执行过event.persist(),所以就进入了分支执行release操作;执行完release后,这个event上的大部分属性就都被清空了然后被放进事件池里。而异步操作是发生在这个过程之后的,这时候如果要访问该event的话 例如我们获取event.target 这时event上的target属性是不存在的了,代码就出错了。
然后再说下事件池;官方文档在说明上述问题时提到了下事件池 :
SyntheticEvent 是合并而来。这意味着 SyntheticEvent 对象可能会被重用,而且在事件回调函数被调用后,所有的属性都会无效。出于性能考虑,你不能通过异步访问事件。
说的比较笼统,解释一下:所有产生的事件都会生成一个事件对象,按正常逻辑 在我们的事件处理函数执行完后,这个事件对象就应该被释放了,等待着被内存回收;但如果在短时间内触发了许多次事件,就要频繁的生成和销毁事件对象;那么 为了提高性能,React就用了一个“事件池”这么一个池子,被使用完后的事件,并不直接销毁,而是将其身上的属性清空掉了后放进事件池中, 等到了下一次有同类型事件发生时,就不用再new一个新的事件对象了,直接从事件池取出一个现成的就可以用了, 从而实现事件对象的重用。
使用这么一套机制最根本的动机在于:在很多业务系统中创建和销毁对象的代价是非常昂贵的。只接触过前端领域的同学可能没怎么听说过XXX对象池这种概念,不过在其他工种的圈子中这个模式被运用在很多地方, 例如后端中经常提及的线程池、数据库连接池,在游戏引擎Unity中也有对象池的概念。 这个模式对于一些场景的性能提升是非常大的,我们想象一下这些场景:Web服务器遇到高并发时,会在瞬时创建和销毁大量的线程、 又或者当我们在愉快地玩耍诸如FPS类型的游戏时,每个弹药都是一个游戏中的对象,那么就会经常会产生大量的对象,并且在短时间内这些对象又会在使用完后等效被销毁,势必就会给游戏的运行带来很大负担;而且很可能还会伴随着长时间的GC,这样的游戏体验可想而知。
关于在异步操作中访问React事件对象的小问题的更多相关文章
- React 事件对象、键盘事件、表单事件、ref获取dom节点、react实现类似Vue双向数据绑定
1.案例实现代码 import React, { Component } from 'react'; /** * 事件对象.键盘事件.表单事件.ref获取dom节点.react实现类似Vue双向数据绑 ...
- JavaScript事件---事件对象
发文不易,若转载传播,请亲注明出处,谢谢! 内容提纲: 1.事件对象 2.鼠标事件 3.键盘事件 4.W3C与IE JavaScript事件的一个重要方面是它们拥有一些相对一致的特点,可以给你的开 ...
- (转)内核线程对象--Event事件对象
在所有的内核对象中,事件内核对象是个最基本的对象.事件能够通知一个操作已经完成. 客户机和一个服务器,它们之间需要互相进行通信例子(vs2008 ) 事件内核对象的组成 一个使用计数(与所有内核对象一 ...
- jquery的click事件对象试解
在写这篇文档的时候,我并没有深入的去了解jquery的事件对象是什么样的构造,不过以我以往的经验,相信能说道说道,并且可能有百分之八十是正确的,所以我并不建议这篇文档具备一定的权威性,不过可以当成饭后 ...
- 第一百二十节,JavaScript事件对象
JavaScript事件对象 学习要点: 1.事件对象 2.鼠标事件 3.键盘事件 4.W3C与IE JavaScript事件的一个重要方面是它们拥有一些相对一致的特点,可以给你的开发提供更多的强大功 ...
- JavaScript(第二十四天)【事件对象】
JavaScript事件的一个重要方面是它们拥有一些相对一致的特点,可以给你的开发提供更多的强大功能.最方便和强大的就是事件对象,他们可以帮你处理鼠标事件和键盘敲击方面的情况,此外还可以修改一般事件的 ...
- node源码详解(七) —— 文件异步io、线程池【互斥锁、条件变量、管道、事件对象】
本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource7 本博客同步在https://cnodejs.o ...
- React对比Vue(03 事件的对比,传递参数对比,事件对象,ref获取DOM节点,表单事件,键盘事件,约束非约束组件等)
import React from 'react'; class Baby extends React.Component { constructor (props) { super(props) t ...
- react事件中的事件对象和常见事件
不管是在原生的js还是vue中,所有的事件都有其事件对象,该事件对象event中包含着所有与事件相关的信息,在react中,所有的事件也有其事件对象,在触发DOM上的某个事件时,就会产生一个事件对象. ...
随机推荐
- 手把手教你NLTK WordNet使用方法
最近看了WordNet,0基础入门,简单记下笔记.因为本身不是学自然语言处理的,好多名词不是特别清楚,现有的博客读的晕乎乎,所以重新写了这个,理清思路.除了行文中提到的链接,其他几个有用的参考链接如下 ...
- troubleshoot之:使用JFR解决内存泄露
目录 简介 一个内存泄露的例子 使用JFR和JMC来分析内存泄露 OldObjectSample 总结 简介 虽然java有自动化的GC,但是还会有内存泄露的情况.当然java中的内存泄露跟C++中的 ...
- C# NPOI计算Execl里面的公式
我这里分两种情况处理 1.Execl中表格中存在公式,我们通过公式获取数据 我们通过Npoi,获取列的属性: private static object GetValueType(ICell cell ...
- 【算法•日更•第二期】查找算法:三分VS二分
▎前言:函数 如果你已经上过初二的数学课了,那么你十有八九会被函数折磨到吐血,这是一种中考压轴题类的题目,往往分类讨论到你恶心.不过没学过也不打紧,现场讲解一下: ☞『数学中的函数』 一般地,如果在一 ...
- js替换指定位置字符串
var str='QWER';//替换WE newstr=replacepos(str,1,2,'XX'); console.log(newstr);//QXXR; function replacep ...
- [PyTorch 学习笔记] 1.3 张量操作与线性回归
本章代码:https://github.com/zhangxiann/PyTorch_Practice/blob/master/lesson1/linear_regression.py 张量的操作 拼 ...
- Flutter FlatButton 按钮基本各种用法
Flutter中给我们预先定义好了一些按钮控件给我们用,常用的按钮如下 RaisedButton :凸起的按钮,其实就是Android中的Material Design风格的Button ,继承自Ma ...
- @SuppressWarnings注解用法详解(转)
原文连接https://blog.csdn.net/sysware_carol/article/details/52100580 今天来谈谈@SuppressWarnings注解的作用. J2SE 提 ...
- JavaScript学习系列博客_8_JavaScript中的关系运算符、赋值运算符
关系运算符 - 关系运算符用来比较两个值之间的关系的 >.<.<=.>= 先说这四个.如果关系成立则返回true,关系不成立则返回false. - 如果比较的两个值是非numb ...
- ubuntu 构建 deb 安装包
源码包下载:http://mirrors.163.com/ubuntu/ 编译工具安装: apt-get install dpkg-dev 以openvswitch为例: wget http://mi ...