TOOLS BOX/ガイド/デバウンス
Concept

デバウンス

連続するイベント発火を抑制し、最後の発火から一定時間後にだけ処理を実行するテクニック。

パフォーマンスイベント制御タイマーsetTimeout

どういう場面で使うか

  • ·テキスト入力のたびに API を叩かないよう間引くとき
  • ·ウィンドウリサイズやスクロールイベントの処理を削減するとき
  • ·フォームの自動保存を最後の入力から N ミリ秒後にだけ実行するとき

注意点 / Pitfalls

  • ·React コンポーネント内で useCallback / useRef を使わずに debounce 関数を生成すると、再レンダリングのたびに新しいタイマーが作られる
  • ·setTimeout の ID を保持して clearTimeout しないとメモリリークやアンマウント後の state 更新が起きる
  • ·テスト時は jest.useFakeTimers() + jest.advanceTimersByTime() で時間を制御する

補足

throttle(一定間隔で間引く)と混同されやすい。debounce は『最後の発火から N ms 後』、throttle は『N ms に 1 回だけ』。

概要

デバウンスは、連続するイベント発火を間引き、最後の発火から指定時間が経過したときだけ処理を実行するテクニックです。 検索入力のたびに API を叩くのではなく、入力が止まってから 300ms 後に1回だけ実行するといった用途に使われます。

基本実装

function debounce<T extends unknown[]>(
  fn: (...args: T) => void,
  delay: number
) {
  let timer: ReturnType<typeof setTimeout> | null = null;
  return (...args: T) => {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  };
}

React での使い方

コンポーネント内では useCallback + useRef で安全に管理します。

import { useCallback, useRef } from "react";

function SearchInput() {
  const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
    if (timerRef.current) clearTimeout(timerRef.current);
    timerRef.current = setTimeout(() => {
      fetchResults(e.target.value);
    }, 300);
  }, []);

  return <input onChange={handleChange} />;
}

throttle との違い

debouncethrottle
実行タイミング最後の発火から N ms 後N ms に 1 回
主な用途検索入力、自動保存スクロール、リサイズ

テスト

jest.useFakeTimers()jest.advanceTimersByTime() で時間を制御してテストします。

jest.useFakeTimers();
fireEvent.change(input, { target: { value: "hello" } });
jest.advanceTimersByTime(300);
expect(mockFetch).toHaveBeenCalledTimes(1);

関連サンプル

同じテーマや技術スタックを使った実装例

関連するサンプルは見つかりませんでした。