デバウンサーとは何か、そしてなぜ重要なのか
デバウンサーとは、時間のかかるタスクが頻繁に実行されないようにするためのプログラミングパターンで、パフォーマンスの問題やサーバーへのリクエストの過負荷を防ぎます。これは、ユーザーが入力するたびに検索リクエストをトリガーするのではなく、ユーザーがタイピングを一時停止した後にトリガーするようにしたい検索入力などのシナリオで特に役立ちます。
デバウンサーを実装することで、ユーザーエクスペリエンスとアプリケーションの効率の両方を向上させることができます。これは、関数が最後に呼び出されてから指定された時間が経過するまで、関数の実行を遅延させることで実現されます。
デバウンサーを使用する場合
デバウンサーは、関数呼び出しの頻度を制限したい状況で使用するのが最適です。一般的な使用例は次のとおりです。
- ユーザー入力の検証: すべてのキーストロークに対して不要な処理を避けるために、ユーザーがタイピングを停止するまで検証を遅延させます。
- 検索機能: ユーザーが検索クエリのタイピングを完了するまで待機して検索プロセスを開始し、サーバーに送信される検索リクエストの数を減らします。
- ウィンドウのリサイズ: ブラウザウィンドウがリサイズされたときに、再計算または調整の数を制限します。
Qwikでデバウンサーを実装する方法
Qwikフレームワークは、シリアライズ可能で効率的な方法で状態とエフェクトを管理するための独自の機能を提供します。Qwikでデバウンサーを実装するには、状態管理にuseSignal
、シリアライズを中断せずに状態をキャプチャできる関数をマークするために$
などのQwikのプリミティブを使用します。以下は単純なデバウンサーパターンです。これは、タイムアウトに達したときに関数を実行するクロージャベースのデバウンサーと同様に動作します。
export const useDebouncer = (fn: QRL<(args: any) => void>, delay: number) => {
const timeoutId = useSignal<number>();
return $((args: any) => {
clearTimeout(timeoutId.value);
timeoutId.value = Number(setTimeout(() => fn(args), delay));
});
};
このデバウンサーは、関数と遅延を引数として受け取ります。timeoutID
を追跡するためにuseSignal
を利用し、Qwikの再開可能性モデルと、QRLの使用との互換性を確保します。返された関数は、呼び出されると既存のタイムアウトをクリアし、指定された遅延後に提供された関数を呼び出すための新しいタイムアウトを設定します。
デバウンサーの使い方
以下の例は、コンポーネントでデバウンサーを使用してユーザー入力を効率的に管理する方法を示しています。入力をデバウンスすることで、アプリケーションはユーザーが1秒間タイピングを停止した後にのみ状態を更新し、API呼び出しやデータフィルタリングなどの操作のパフォーマンスを最適化します。
import { $, useSignal, component$, type QRL } from "@builder.io/qwik";
import { useDebouncer } from "~/utils/debouncer";
export default component$(() => {
const debouncedValue = useSignal("");
const debounce = useDebouncer(
$((value: string) => {
debouncedValue.value = value;
}),
1000
);
return (
<>
<input
onInput$={(_, target) => {
debounce(target.value);
}}
/>
<p>{debouncedValue.value}</p>
</>
);
});
ライブデモ
以下のライブデモでは、useDebouncer
は、最後のキーストロークから1秒の遅延が経過した後にdebouncedValue
シグナルを更新するために使用されます。
useDebouncer$
ボーナス:開発者が余分な「$()でラップする」というアクションを回避するために。
Qwikのimplicit$FirstArg
関数を利用して、提供された関数を自動的に$()
でラップするuseDebouncer$
関数を作成できます。
これは、Qwikが実際にすべての組み込みの$フックを実装する方法です。
export const useDebouncerQrl = (fn: QRL<(args: any) => void>, delay: number) => {
const timeoutId = useSignal<number>();
return $((args: any) => {
clearTimeout(timeoutId.value);
timeoutId.value = Number(setTimeout(() => fn(args), delay));
});
};
export const useDebouncer$ = implicit$FirstArg(useDebouncerQrl);
そして、今は
const debounce = useDebouncer$(
(value: string) => {
debouncedValue.value = value;
},
1000
);
素晴らしいですよね?:)