SWR と React Select でオートコンプリート機能付きプルダウンを実装する
React Select の Async コンポーネントを利用してオートコンプリート機能付きのプルダウンを実装する場合に SWR のキャッシュ機能を利用する方法を紹介します。
SWR を利用することで、プルダウンコンポーネントをページ内で複数利用した場合でも、無駄なHTTPリクエストが発生することがないため、パフォーマンスの向上やサーバーの負荷低減が期待できます。
ライブラリのインストール
npm 経由で react-select
と swr
をインストールします。
$ npm i react-select swr
SWR で取得したデータをプルダウンで表示する
次のコードがSWR で取得したデータをプルダウンで表示する機能を実装したコードになります。
例として、カラーを入力するための ColorSelect
コンポーネントを実装します。
import AsyncSelect from 'react-select/async';
import useSWR from 'swr';
import type { GroupBase, OptionsOrGroups, Props } from 'react-select';
import { useCallback, useEffect, useState } from 'react';
export interface ColourOption {
readonly value: string;
readonly label: string;
}
export const colourOptions: readonly ColourOption[] = [
{ value: 'ocean', label: 'Ocean' },
{ value: 'blue', label: 'Blue' },
{ value: 'purple', label: 'Purple' },
{ value: 'red', label: 'Red' },
{ value: 'orange', label: 'Orange' },
{ value: 'yellow', label: 'Yellow' },
{ value: 'green', label: 'Green' },
{ value: 'forest', label: 'Forest' },
{ value: 'slate', label: 'Slate' },
{ value: 'silver', label: 'Silver' },
];
/**
* サーバーから非同期でオプションを取得するモック関数
*/
function fetchColorOptions(inputValue: string) {
return new Promise<ColourOption[]>((resolve) => {
setTimeout(() => {
const options = colourOptions.filter((option) =>
option.label.toLowerCase().includes(inputValue.toLowerCase())
);
resolve(options);
}, 1000);
});
}
async function fetcher({ inputValue }: { inputValue: string }) {
const options = await fetchColorOptions(inputValue);
return options;
}
function ColorSelect() {
const [inputValue, setInputValue] = useState<string>('');
const { data: options, error } = useSWR({ inputValue }, fetcher);
const loadOptions = useCallback(
(
_: string,
callback: (options: OptionsOrGroups<unknown, GroupBase<unknown>>) => void
): void => {
if (error) {
callback([]);
return;
}
if (options === undefined) {
callback([]);
return;
}
callback(options);
return;
},
[options, error]
);
const handleInputChange = useCallback(
(newValue: string) => {
setInputValue(newValue);
},
[setInputValue]
);
return (
<div className="container">
<AsyncSelect
isClearable
isSearchable
cacheOptions
defaultOptions={options}
loadOptions={loadOptions}
onInputChange={handleInputChange}
/>
</div>
);
}
export default ColorSelect;
fetchColorOptions
をサーバーからオプションを取得する処理に置き換えることでページ内で ColorSelect
コンポーネントを複数利用した場合でも、1度目のリクエスト移行は SWR のキャッシュからプルダウンのデータを取得できます。
import ColorSelect from 'path/to/color-select'
export default function Page() {
return (
<form>
<ColorSelect />
<ColorSelect />
<ColorSelect />
<ColorSelect />
<ColorSelect />
</form>
);
}
loadOptions の callback 引数を利用する
ポイントは loadOptions
の callback
引数を利用している点です。 callback
引数を利用することで、 loadOptions
関数の中でデータを取得せず、 loadOptions
関数外の SWR で管理しているデータをプルダウン用のデータとして利用することができます。
SWR のデータは inputValue
を Reactの State として管理することで、inputValue
に変更がある場合に SWR のデータを更新しています。
inputValue
は React Select の onInputChange
props に更新用のコールバック関数を設定することで、ユーザーが入力を行うたびに inputValue
を更新しています。
まとめ
今回は React Select と SWR を組み合わせてオートコンプリート機能付きプルダウンコンポーネントを実装する方法を紹介しました。
SWR を利用することでコンポーネントを複数利用した場合に、無駄なHTTPリクエストを削減することができます。既存の React Select で実装しているコンポーネントのパフォーマンス改善として、ぜひ取り入れてみてください。
Discussion