Reactでキーワードサジェストを作る
はじめに
こんにちは!
スペースマーケットでフロントエンドエンジニアをしているk___0122です。
過去に以下のようなキーワードサジェストを実装したので、Reactを使ってキーワードサジェストを実装する方法を簡単にですが紹介します。
具体的には、以下2つについて説明します。
- ユーザーが入力を終えてから一定時間経ってからリクエストを送信する方法
- キーボードで候補を選択できるようにする方法
ユーザーが入力を終えて一定時間経ってからリクエストを送信する
ユーザーが入力した文字をそのままリクエストに使うと、リクエストが連続して発生するため、サーバーに負荷をかける原因になります。
そこで、一定時間(例えば、500ミリ秒)空けた後にリクエストを送信するようにすることで、負荷を軽減できます。
今回はreact-useのuseDebounceというカスタムフックを使います。
useDebounceは第二引数に指定したミリ秒後に第一引数のコールバック関数を実行します。
以下はuseDebounceを使った実装になります。
import React, { useState } from 'react';
import { useDebounce } from 'react-use';
export const AutocompleteDemo = () => {
const [keyword, setKeyword] = useState('');
const [suggestions, setSuggestions] = useState<string[]>([]);
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setKeyword(event.target.value);
};
// keywordの値が変更されたら500ms後にfetchSuggestionsを呼び出す
useDebounce(
() => {
// APIを呼び出す
const fetchSuggestions = async () => {
const response = await fetch('https://dummyapi.example.com');
const data = await response.json();
setSuggestions(data);
};
fetchSuggestions();
},
500,
[keyword]
);
return (
<>
<input type="search" value={keyword} onChange={handleInputChange} />
<ul>
{suggestions.map((suggestion) => (
<li key={suggestion}>{suggestion}</li>
))}
</ul>
</>
);
};
キーボードで操作できるようにする
サジェストはクリックのみではなく、キーボードで候補を選択できるようにしました。
具体的には、以下のような手順で実装します。
- キーボードの上下キーでカーソルを移動する
- Escapeを押すとサジェストが閉じる
- Enterで選択したキーワードを入力欄にセットしAPIを叩く
以下はkeyDownの処理を追加したものになります。
const [keyword, setKeyword] = useState("");
const [suggestions, setSuggestions] = useState<string[]>([]);
const [focusedIndex, setFocusedIndex] = useState(-1);
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setKeyword(event.target.value);
};
// ...
const handleOnKeyDown = (
e: React.KeyboardEvent<HTMLInputElement>
) => {
const eventKey =
e.keyCode === 13 ? "ConfirmEnter" : e.key;
if (
eventKey === 'ArrowUp' ||
eventKey === 'ArrowDown' ||
eventKey === 'ConfirmEnter' ||
eventKey === 'Escape'
) {
e.preventDefault()
}
switch (eventKey) {
case "ArrowUp":
setFocusedIndex((prevIndex) =>
prevIndex <= 0 ? suggestions.length - 1 : prevIndex - 1
);
break;
case "ArrowDown":
setFocusedIndex((prevIndex) =>
prevIndex === suggestions.length - 1 ? 0 : prevIndex + 1
);
break;
case "ConfirmEnter":
// ここで検索のAPIを叩く
break;
case "Escape":
setSuggestions([]);
setFocusedIndex(-1);
break;
}
};
// keywordの値が変更されたら500ms後にfetchSuggestionsを呼び出す
useDebounce(
// ...
);
// ...
return (
<input
type="search"
value={keyword}
onChange={handleInputChange}
onKeyDown={handleOnKeyDown}
/>
SafariのみIME確定時のEnterとAPIを叩く際のEnterがうまく制御できなかったので、keyCodeで判断しました。
IME確定時のEnterの制御について過去にこちらでまとめているのでよかったらご覧ください!
最後に
スペースマーケットでは、一緒にサービスを成長させていく仲間を探しています。
とりあえずどんなことをしているのか聞いてみたいという方も大歓迎です!
ご興味ありましたら是非ご覧ください!
スペースを簡単に貸し借りできるサービス「スペースマーケット」のエンジニアによる公式ブログです。 弊社採用技術スタックはこちら -> whatweuse.dev/company/spacemarket
Discussion
ここの実装は横展が効くように
cycle
関数などにしてもありかなと思いました。キー操作ではなく、ボタン駆動ですが、demo code.