debounce 函數(shù)引起的思考
開發(fā)中我們經(jīng)常遇到這種情況,一個輸入框內(nèi)觸發(fā)onChange事件時調用接口章蚣,或者處理某件事情。但是我們就會發(fā)現(xiàn)每當我們每當輸入一個單詞的時候就會發(fā)現(xiàn)觸發(fā)了這個動作,是不是體驗不是很友好,如果等用戶輸入完了在執(zhí)行那該多好啊岳链,那我們可以在光標離開的時候觸發(fā),但是往往事與愿違劲件。產(chǎn)品說我想讓他實時搜索掸哑,那...约急。那是不是可以使用延時的方法,比如使用settimeout方法延時執(zhí)行這個方法苗分,比如延時1秒執(zhí)行厌蔽。那我們就可以使用計時器的方式去實現(xiàn)。
- 我們可以看下面那個例子俭嘁,我們每輸入一個字符躺枕,控制臺就會打印一次服猪。
打開鏈接供填,可以看到具體實現(xiàn)
https://codesandbox.io/embed/x9q5nl2lv4
現(xiàn)在我們把讓面的代碼用下面的替換,然后在看控制臺的打印結果罢猪。
import React from "react";
import ReactDOM from "react-dom";
import { Input } from "antd";
import "./styles.css";
const Search = Input.Search;
class App extends React.Component {
constructor(props) {
super(props);
this.waitSearch = null;
}
handleOnSearch = e => {
const value = e.target.value;
if (this.waitSearch) {
clearTimeout(this.waitSearch);
}
this.waitSearch = setTimeout(() => console.log(value), 1000);
};
render() {
return (
<div>
<Search
placeholder="input search text"
onChange={this.handleOnSearch}
style={{ width: 200 }}
/>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
我們聲明了一個全局變量waitSearch來控制settimeout的執(zhí)行近她,在你頻繁輸入的時候我們將你之前的任務都給清楚掉。這樣最后一個action將在你不觸發(fā)后的某個時間執(zhí)行膳帕。
- 使用過lodash的同學就會發(fā)現(xiàn)有一個debounce函數(shù)可以滿足我們現(xiàn)在的場景,我們將上面的代碼替換成下面的代碼粘捎。
import React from "react";
import ReactDOM from "react-dom";
import { Input } from "antd";
import "./styles.css";
import _ from "lodash";
const Search = Input.Search;
class App extends React.Component {
constructor(props) {
super(props);
this.debouncePrint = _.debounce(this.print, 1000);
}
handleOnSearch = e => {
const value = e.target.value;
this.debouncePrint(value);
};
print = value => {
console.log(value);
};
render() {
return (
<div>
<Search
placeholder="input search text"
onChange={this.handleOnSearch}
style={{ width: 200 }}
/>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
我們看到這句代碼,我們將print函數(shù)變成了debounce的了危彩。
this.debouncePrint = _.debounce(this.print, 1000);
我們將一個函數(shù)傳進去攒磨,然后返回了一個新的函數(shù)。那我們可以使用這個思路自己實現(xiàn)一個自己的debounce汤徽。
import React from "react";
import ReactDOM from "react-dom";
import { Input } from "antd";
import "./styles.css";
const Search = Input.Search;
class App extends React.Component {
constructor(props) {
super(props);
this.debouncePrint = this.debounce(this.print, 1000);
}
handleOnSearch = e => {
const value = e.target.value;
this.debouncePrint(value);
};
print = value => {
console.log(value);
};
debounce(fn, time) {
let last = null;
const that = this;
return function(...args) {
if (last) {
clearTimeout(last);
}
last = setTimeout(() => {
fn.apply(that, args);
}, time);
};
}
render() {
return (
<div>
<Search
placeholder="input search text"
onChange={this.handleOnSearch}
style={{ width: 200 }}
/>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
是不是很好用娩缰,其實我們可以在進行深入考慮一下,我們知道Decorator修飾器可以修飾一個函數(shù)谒府,其實也是高階函數(shù)應用之一拼坎,只是修飾器使用了編譯特性,將函數(shù)上下文注入完疫。如果我們使用裝飾器的模式來寫一個debouce函數(shù)泰鸡,是不是更加簡單哪?
import React from "react";
import ReactDOM from "react-dom";
import { Input } from "antd";
import "./styles.css";
const Search = Input.Search;
const Debounce = (time: number) => {
let last = null;
return (_target, _property, descriptor) => {
const fn = descriptor.value;
descriptor.value = function(...args) {
const that = this;
clearTimeout(last);
last = setTimeout(function() {
fn.apply(that, args);
}, time);
};
return descriptor;
};
};
class App extends React.Component {
handleOnSearch = e => {
const value = e.target.value;
this.print(value);
};
@Debounce(1000)
print(value) {
console.log(value);
}
render() {
return (
<div>
<Search
placeholder="input search text"
onChange={this.handleOnSearch}
style={{ width: 200 }}
/>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
是不是更加簡單壳鹤,我們可以充分使用修飾器的功能盛龄,創(chuàng)造出一個個帶有特殊功能的函數(shù),讓你解放雙手芳誓。
使用redux-saga
redux-saga這個中間件可以很好的為我們處理異步邏輯余舶,當然也可以幫我們進行debounce。
function* handleInput({ input }) {
// debounce by 500ms
yield call(delay, 500)
...
}
function* watchInput() {
yield takeLatest('INPUT_CHANGED', handleInput);
}
使用redux-observable
redux-observable是以rxjs進行的一個控制異步流程的插件兆沙,這個更加強大欧芽,當然也更難掌握,需要整個開發(fā)團隊的水平比較高葛圃,下面是rxjs5的寫法千扔。
const inputEpic = (action$) =>
action$.ofType('INPUT_CHANGED')
.debounceTime(500)
必備知識
- 高階函數(shù)
- apply函數(shù)的使用
- decorator的使用