在 react 組件中使用 debounce 函數

什麼是 debounce

debounce,中文翻譯爲防抖。與之相對應的還有一個概念 throttle —— 節流。二者用來控制某個函數在必定時間內執行多少次的技巧,類似卻又不一樣。javascript

關於二者的對比以及使用場景,能夠閱讀這篇文章來加深瞭解。本文主要講述的,是若是在 react components中,使用 debounce 函數來防止回調事件的屢次觸發,從而提高代碼效率。css

應用

在一個 input 輸入框中,給其綁定了一個 onChange 事件,每次輸入後,都會觸發一個回調函數。由於咱們想要的只是用戶輸入的最後結果,因此除了用戶輸入完成後觸發的回調函數,其餘都是冗餘的。所以,須要對綁定的回調函數作 debounce 處理。java

關於 debounce 函數,有不少實現版本,這裏選用了 loadsh.debounce。雖然函數實現原理很簡單,可是用於生產環境的代碼,仍是建議使用成熟的第三方庫。react

咱們的第一反應,通常都是直接將回調函數用 debounce 包裹起來達到想要的效果。ajax

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>
    );
  }
}
相關文章
相關標籤/搜索