[Remix] Remixでインクリメンタルサーチを実装する
情報システム室の進地@日比谷です。
Remixでインクリメンタルサーチを実装する方法について記述します。
仕様
Homeページに設置した検索ボックスでインクリメンタルサーチを実装します。会社の情報が入ったDBがあるとして、会社名で候補検索する仕様を想定します。
ファイル構成
Remixの標準構成に app/model/company.ts
と app/routes/companies.tsx
を追加しています。
前者はCompanyデータ形と会社データのサンプルを記載し、後者には会社データを取得するloaderを記載しています。
app ├── entry.client.tsx ├── entry.server.tsx ├── models │ └── company.ts ├── root.tsx └── routes ├── _index.tsx └── companies.tsx
コード
idとnameを文字列としてもつCompany
型を定義しています。また、getCompanies()
メソッドでダミーの会社情報を返すようにしています。
ここを外部DBなどから取得するように変更すれば当該DBに対してインクリメンタルサーチができるようになります。
export type Company = { id: string; name: string; }; export async function getCompanies(): Promise<Array<Company>> { return [ { id: "aaa", name: "ウエキン合同会社", }, { id: "bbb", name: "ウエキンコーポレーション", }, { id: "ccc", name: "シンチ有限会社", }, ]; }
loaderを定義します。GETパラメータのterm
から検索文字列を受け取り、先述のgetCompanies()
で取得したデータに対してフィルタして返しています。
import { json, LoaderFunctionArgs } from "@remix-run/node"; import { Company, getCompanies } from "~/models/company"; export const loader = async ({ request, }: LoaderFunctionArgs) => { const url = new URL(request.url); const term = url.searchParams.get('term'); return json(await searchCompany('' + term)); } async function searchCompany(term: string): Promise<Array<Company>> { const companies = await getCompanies(); return companies.filter(company => company.name?.match(term)); }
最後に検索窓を置くHome画面(Index)の実装です。
fetcherを使って、/companies?term=検索ワード
の呼び出しを行い、結果をfetcher.data
で受け取ってレンダリングします。
<input type="search">
と<datalist>
を組み合わせることで簡単に候補表示が可能です。
import type { MetaFunction } from "@remix-run/node"; import { useFetcher } from "@remix-run/react"; import { Company } from "~/models/company"; export const meta: MetaFunction = () => { return [ { title: "Home" }, { name: "description", content: "This is your portal site!" }, ]; }; export default function Index() { const fetcher = useFetcher(); return ( <div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}> <fetcher.Form> <input type="search" onChange={(e) => { const term = e.currentTarget.value; fetcher.load(`/companies?term=${term}`); }} list="suggestions" /> <datalist id="suggestions"> {fetcher.data?.map((company: Company) => { return ( <option key={company.id} value={company.name} /> ) })} </datalist> </fetcher.Form> </div> ); }