🏈
TypeScript で引数まで含めた関数呼び出しごとに debounce したい!
次のような関数を定義しておけばよい。
debounceBasedOnArgs.ts
import type { DebounceSettings, DebouncedFunc } from 'lodash';
import debounce from 'lodash.debounce';
import memoize from 'lodash.memoize';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function debounceBasedOnArgs<R, F extends (...args: any[]) => R>(
fn: F,
wait = 0,
options: DebounceSettings = {}
): (...args: Parameters<F>) => ReturnType<F> | undefined {
const debouncedAndMemoized = memoize<(...args: Parameters<F>) => DebouncedFunc<F>>(
function debounced() {
return debounce(fn, wait, options);
},
(...args: Parameters<F>) => JSON.stringify(args)
);
return function wrappedFunction(...args: Parameters<F>): ReturnType<F> | undefined {
return debouncedAndMemoized(...args)(...args);
};
}
使い方は次のように。
const logEvent = debounceBasedOnArgs(
(eventName: string, parameters: Record<string, string>) => {
gtag("event", eventName, parameters);
},
100
);
logEvent("tap_item", {
name: "Anpanman",
});
logEvent("tap_item", {
name: "Currypanman",
});
logEvent("tap_item", {
name: "Shokupanman",
});
logEvent("tap_item", {
name: "Anpanman",
});
こうすることで Anpanman
の tap_item
イベントは一度しか記録されないようになる。
解説
元ネタはlodash の GitHub issue にあったこのコメント 。
memoize
関数の第 2 引数である resolver
に、引数を元にユニークな文字列を作るための関数を指定している。 memoize
は内部で引数と関数の結果をペアにしてキャッシュしているが、デフォルトではその際に使われる引数は第 1 引数のみとなっている。今回やりたいこととは違うので resolver
を指定している。ほかは理解しやすいよう、型定義を多少いじっている。
Discussion