专栏首页OECOMreact移除监听事件无效

react移除监听事件无效

问题描述

首先先来复现一下代码,背景是这样的,需要监听一下scroll事件,但是监听函数需要当前组件的this环境,所以监听函数上需要bind(this), 但是这样操作后发现无法removeEventListener这个监听,提示: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.

handleScroll(){
    this.setStaete()  //举例不一定用到setState
    }
 componentWillMount(){
        window.addEventListener('scroll', this.handleScroll.bind(this));
    }

    /** 组件销毁后必要的清理*/
    componentWillUnmount(){
        window.removeEventListener('scroll', this.handleScroll.bind(this))
    }

当我这样写了以后发现销毁组件的时候无法销毁。其实原因就在于添加了bind(this),如果没有他就会很方便的移除,一旦加上了bind(this),就失败了。

addEventListener、removeEventListener与事件处理程序

首先先来了解一下这两个函数的使用。addEventListener()和removeEventListener()是“DOM2级事件”中定义的两个方法,分别用于添加和删除事件处理程序。所有的DOM节点中都包含这两个方法,并且它们都接受3个参数:要处理的事件名、要为事件添加的处理程序的函数和一个表示事件处理阶段的布尔值。

这里重点是添加处理的函数,addEventListener()和removeEventListener()添加的处理函数必须是同一个函数,什么叫同一个函数呢,就是说这两个函数时相等的,指向同一个地址。我们都知道匿名函数是无法移除的,原因就在于此,直接添加的匿名函数时无法实现另一个匿名函数和此匿名函数相等。

window.addEventListener('scroll', function(e){
    console.log(e)
});
window.removeEventListener('scroll', function(e){
    console.log(e)
});

如上代码所示,虽然两个匿名函数写的一样,但是在程序上来说,这两个匿名函数是不相同的,所以匿名函数无法移除,这就是根源所在。

那么我们再来看一下为何加了bind(this)之后也无法移除呢。那先来看看bind的作用。

const test = {
    name:'oecom',
    getName:function(){
        console.log(this.name)
    }
}
test.getName()//输出oecom

然后我们对代码进行稍加改动

const test = {
    name:'oecom',
    getName:function(){
        console.log(this.name)
    }
}
const func = test.getName;
func();//这里直接输出了undefined
const func1 = test.getName.bind(test);
func1();//这里直接输出oecom

很明显,bind是为了指明this的指向,解决this丢失的问题。那么为何会加上bind之后无法移除呢,我们来看一下下面的两个代码

let func1 = test.getName.bind(test);
let func2 = test.getName.bind(test);
let func3 = test.getName;
let func4 = test.getName;
console.log(func1==func2)
console.log(func3==func4)

我想看到了上图的输出结果,大家应该明白了为何加上bind之后会无法移除监听事件了,其根源就在于每次加上bind之后返回的函数并不是指向同一个函数

解决方案

既然明白了原因所在,那么我们来说一下如何解决。具体思路有两个,其中一个是在constructor中提前声明好:

constructor(){
    super();
    this.scrollEvent = this.handleScroll.bind(this)
}
handleScroll(){
    this.setStaete()  //举例不一定用到setState
    }
 componentWillMount(){
        window.addEventListener('scroll', this.scrollEvent);
    }

    /** 组件销毁后必要的清理*/
    componentWillUnmount(){
        window.removeEventListener('scroll', this.scrollEvent)
    }

另一种方式则是使用箭头函数了:

handleScroll = ()=>{
    this.setStaete()  //举例不一定用到setState
    }
 componentWillMount(){
        window.addEventListener('scroll', this.handleScroll);
    }
    /** 组件销毁后必要的清理*/
    componentWillUnmount(){
        window.removeEventListener('scroll', this.handleScroll)
    }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • React router 4.0之参数传递

    在前一篇文章中说到了react router 4的路由如何配置,这篇文章说一下路由跳转的参数问题。路由跳转传参一种方式是在link标签上写参数,另一种方式是通过...

    无邪Z
  • call和 apply的区别是什么

    一、方法的定义 call方法: 语法:call(thisObj,Object) 定义:调用一个对象的一个方法,以另一个对象替换当前对象。 说明: ca...

    无邪Z
  • javasrcipt删除数组元素splice函数

    在进行javascript中删除数组元素有两个方法,一个是delete,另一个是splice函数。这两个的区别在于一个删除的干净,一个删除的不干净。所谓的干净与...

    无邪Z
  • 浅谈react 中的 this 指向

    https://react.docschina.org/docs/react-without-jsx.html

    念念不忘
  • React-实现上拉加载更多

    yuezhongbao
  • nodejs 14.0.0源码分析之FixedQueue

    theanarkh
  • 浅谈window桌面GUI技术及图像渲染性能测试实践

    从Windows Vista之后,desktop composition的部分就由Desktop Window Manager完成了(当然是启用Aero的情况下...

    高楼Zee
  • vue methods 方法中 方法 调用 另一个方法。

    this.$options.methods.test2();一个方法调用另外一个方法;

    小蔚
  • SSM 单体框架 - 前端开发:课程和广告模块

    使用 Element UI 表格进行数据展示:https://element.eleme.cn/#/zh-CN/component/table

    RendaZhang
  • vue归纳笔记:对vue中nextTick()的理解及应用场景说明

    请记住:vue是依靠数据驱动视图更新的,该更新的过程是异步的。即:当侦听到你的数据发生变化时, Vue将开启一个队列(该队列被Vue官方称为异步更新队列)。视图...

    用户1272076

扫码关注云+社区

领取腾讯云代金券