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
和瀏覽器原生事件同樣有 stopPropagation()
、preventDefault()
接口,並且這些接口誇瀏覽器兼容。異步
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> ); } } |