在 react 组件中使用 debounce 函数

2018/10/29 20:50
阅读数 3.6K

什么是 debounce

debounce,中文翻译为防抖。与之相对应的还有一个概念 throttle —— 节流。两者用来控制某个函数在一定时间内执行多少次的技巧,相似却又不同。

关于两者的对比以及使用场景,可以阅读这篇文章来加深了解。本文主要讲述的,是如果在 react components中,使用 debounce 函数来防止回调事件的多次触发,从而提升代码效率。

应用

在一个 input 输入框中,给其绑定了一个 onChange 事件,每次输入后,都会触发一个回调函数。因为我们想要的只是用户输入的最后结果,所以除了用户输入完成后触发的回调函数,其他都是冗余的。因此,需要对绑定的回调函数做 debounce 处理。

关于 debounce 函数,有很多实现版本,这里选用了 loadsh.debounce。虽然函数实现原理很简单,但是用于生产环境的代码,还是建议使用成熟的第三方库。

我们的第一反应,一般都是直接将回调函数用 debounce 包裹起来达到想要的效果。

import react, { Component } from 'react';
import { debounce } from 'lodash.debounce';

export default class Debounce extends Component {
  printChange(e) {
    console.log('value :: ', e.target.value);
    // call ajax
  }
  render() {
    return (
      <div>
        <input onChange={debounce(this.printChange, 500)} />
      </div>
    );
  }
}

但是这么做之后,浏览器就会报异常: Uncaught TypeError: Cannot read property 'value' of null。为什么会这样?这里就涉及到了 react 事件系统 中的一个概念:合成事件。

 

合成事件(SyntheticEvent)

事件处理程序通过 合成事件(SyntheticEvent)的实例传递,SyntheticEvent 是浏览器原生事件跨浏览器的封装。SyntheticEvent 和浏览器原生事件一样有 stopPropagation()preventDefault() 接口,而且这些接口夸浏览器兼容。

事件池(Event Pooling)

SyntheticEvent 是池化的. 这意味着 SyntheticEvent 对象将会被重用,并且所有的属性都会在事件回调被调用后被 nullified。 这是因为性能的原因。 因此,你不能异步的访问事件。

通过了解事件系统,也就不难理解为什么会报错了。因为经过 debounce 包装后的回调函数,变成了一个异步事件,在池化后被 nullified 了。

那么怎样才能解决这个问题?

通过在回调事件顶部加上 e.persist() 就可以从池中移除合成事件,并允许对事件的引用保留。

import react, { Component } from 'react';
import { debounce } from 'lodash.debounce';

export default class Debounce extends Component {
  printChange(e) {
    console.log('value :: ', e.target.value);
    // call ajax
  }
  render() {
    return (
      <div>
        <input onChange={debounce(this.printChange, 500)} />
      </div>
    );
  }
}

 

这样做之后报错问题虽然解决了,但是会发现 debounce 的函数并没有被执行。因此,还需要对回调函数进行优化。

把需要异步执行的回调函数抽离出来封装,并且在组件初始化话的时候就将其 debounce 化,就可以得到我们想要的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import react, { Component } from 'react';
import { debounce } from 'lodash.debounce';

export default class Debounce extends Component {
  construtor() {
    super();
    this.callAjax = debounce(this.callAjax, 300);
  }
  
  callAjax = (value) => {
    console.log('value :: ', value);
    // call ajax
  }
  printChange(e) {
    e.persist();
    this.callAjax(e.target.value);
  }
  render() {
    return (
      <div>
        <input onChange={this.printChange} />
      </div>
    );
  }
}
展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
1 收藏
0
分享
返回顶部
顶部