最近接觸umi,在使用umi搭建後臺項目時發現,有一個需求是在input框輸入時進行防抖查詢。可是Antd的input框,onChange事件觸發返回的callback若是用防抖函數包裹的話,event對象的target屬性是null。react
效果以下:ajax
<Input
placeholder="任務ID"
onChange={debounce(e => {
console.log(e)
if (e.target) {
let q = e.target.value;
setPendingId(q || undefined)
}
}, 500)}
allowClear
/>
複製代碼
打印結果:瀏覽器
很明顯target是null,爲何正常callback能夠獲取到,使用debouce函數就出現如此怪異的現象?bash
查閱一番以後得知,原來是React的合成事件(SyntheticEvent)致使的。異步
合成事件(SyntheticEvent)函數
事件處理程序經過 合成事件(SyntheticEvent)的實例傳遞,SyntheticEvent 是瀏覽器原生事件跨瀏覽器的封裝。SyntheticEvent 和瀏覽器原生事件同樣有 stopPropagation()、preventDefault() 接口,並且這些接口誇瀏覽器兼容。性能
事件池(Event Pooling)ui
SyntheticEvent 是池化的. 這意味着 SyntheticEvent 對象將會被重用,而且全部的屬性都會在事件回調被調用後被 nullified。 這是由於性能的緣由。 所以,你不能異步的訪問事件。this
經過了解事件系統,也就不難理解爲何會報錯了。由於通過 debounce 包裝後的回調函數,變成了一個異步事件,在池化後被 nullified 了。spa
那麼怎樣才能解決這個問題?
經過在回調事件頂部加上 e.persist() 就能夠從池中移除合成事件,並容許對事件的引用保留。而且把須要異步執行的回調函數抽離出來封裝,而且在組件初始化話的時候就將其 debounce 化,就能夠獲得咱們想要的效果。
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>
);
}
}
複製代碼
由於我使用的是Hooks,因此換了個想法,使用ref,直接上代碼
// 使用iseRef
import React, { useState, useEffect, useRef } from 'react';
const fileInputEl: any = useRef(null)
const callAjax = (value) => {
setPendingId(value || undefined);
}
let printChange = debounce((e) => {
e.persist()
console.log(fileInputEl);
callAjax(fileInputEl.current.input.state.value)
}, 500)
<Input
placeholder="任務ID"
ref={fileInputEl}
onChange={printChange}
allowClear
/>
複製代碼
打印結果
後面發現只要不直接用debounce包裹回調函數,使用函數調用傳參的方法就能夠拿到e.target了。
const printChange = (e) => {
taskChange(e.target.value)
}
const taskChange = debounce((value) => {
setPendingId(value || undefined);
}, 500)
<Input
placeholder="任務ID"
ref={fileInputEl}
onChange={printChange}
allowClear
/>
複製代碼
這裏就能夠拿到input中的value了,哈哈,以爲有用的話就給個贊吧。