1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Reactパフォーマンス最適化】デバウンスを身に着けよう

Last updated at Posted at 2025-10-22

突然ですが質問です。


質問:
ユーザがサーチバーに入力するたびにAPIを呼び出しています。
これではサーバに過剰な負荷がかかります。
どう最適化すればよいでしょうか?


こんな質問をされたらどう答えますか?

会話の流れ

面接官:
「サーチバーでユーザが入力するたびにAPIが呼ばれています。
これがサーバに負荷をかけています。どうすればよいでしょう?」

エンジニア:
「たしかに、毎回リクエストが飛ぶと効率が悪いですね。」
「**Debouncing(デバウンス)**を使うと良いです。
これは“入力が止まってから一定時間(例:500ms)経過したら関数を実行する”方法です。
たとえば商品検索でも、タイプ中はAPIを呼ばず、止まってから1回だけ検索を実行します。」

面接官:
「なるほど! サーバも軽くなるし、動作も安定しそうですね。」


Debouncingとは?

状況 挙動 特徴
デバウンスなし 入力のたびにAPI呼び出し サーバ負荷が高い
デバウンスあり 入力が止まってから1回だけ呼び出し 無駄がなく効率的

React実装例(実際に動く)

使用API:JSONPlaceholder
エンドポイント: /users
このAPIは「ユーザー情報(名前やメールなど)」を返します。


codeSandBoxにコピペで試せるので是非

react環境を構築し、App.jsxに今記事のコードをコピペするだけで手軽に試せます

デバウンスなしの検索

import React, { useState, useEffect } from "react";

// デバウンスなしバージョン
const App = () => {
  const [query, setQuery] = useState("");
  const [results, setResults] = useState([]);

  useEffect(() => {
    if (!query) {
      setResults([]);
      return;
    }

    const fetchUsers = async () => {
      try {
        const res = await fetch("https://jsonplaceholder.typicode.com/users");
        const data = await res.json();
        const filtered = data.filter((user) =>
          user.name.toLowerCase().includes(query.toLowerCase())
        );
        setResults(filtered);
      } catch (err) {
        console.error("API error:", err);
      }
    };

    fetchUsers();
  }, [query]);

  return (
    <div className="p-4 border rounded w-full">
      <h2 className="font-bold text-lg mb-2">デバウンスなし</h2>
      <input
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="ユーザー名を入力"
        className="border p-2 w-full"
      />
      <ul className="mt-2">
        {results.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
};

export default App;

実行結果

Videotogif.gif

各入力のたびに(即座に)検索結果が表示されていることがわかりますね


デバウンスありの検索(最適化)

import { useState, useEffect } from "react";

export const App = () => {
  const [query, setQuery] = useState("");
  const [debouncedQuery, setDebouncedQuery] = useState("");
  const [results, setResults] = useState([]);

  // 入力が止まってから500ms後に反映
  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedQuery(query);
    }, 500);
    return () => clearTimeout(timer);
  }, [query]);

  useEffect(() => {
    if (!debouncedQuery) {
      setResults([]);
      return;
    }

    const fetchUsers = async () => {
      try {
        const res = await fetch("https://jsonplaceholder.typicode.com/users");
        const data = await res.json();
        const filtered = data.filter((user) =>
          user.name.toLowerCase().includes(debouncedQuery.toLowerCase())
        );
        setResults(filtered);
      } catch (err) {
        console.error("API error:", err);
      }
    };

    fetchUsers();
  }, [debouncedQuery]);

  return (
    <div className="p-6">
      <h2 className="font-bold text-lg mb-2">デバウンスあり(500ms遅延)</h2>
      <input
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="ユーザー名を入力"
        className="border p-2 w-full"
      />
      <ul className="mt-4">
        {results.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
};

実行結果

Videotogif (1).gif

入力が完了されてから検索結果が表示されているのがわかりますね

比較項目 デバウンスなし デバウンスあり
API呼び出し頻度 文字入力のたび 入力停止後のみ
サーバ負荷 高い 低い
体感UX カクつく なめらか

※ 一長一短ではなく、ケースバイケースではあります。
目的によっては毎回のfetchがあってもいいかもしれません。
常に何がこの場における最適化か?を自問自答するようにいただければと思います。


まとめ

  • デバウンスを導入するだけでAPIの呼び出し頻度を劇的に削減できる

  • 入力完了を待つことで、ユーザ体験も向上する

  • 実務ではさらに

    • AbortControllerで古いリクエストをキャンセル
    • React QuerySWRでキャッシュ最適化
      などを組み合わせるとさらにgood

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?