僕のようなバックエンド開発スタートのWebエンジニアからすると検索機能=バックエンドアプリケーションでの役割と思ってしまいます(思いますよね?)が、フロントエンドで完結する検索機能を実装するもできるのでその実装サンプルを解説します。(言語はTypeScriptを使います)

Reactフロントエンドでのリアルタイム検索を実装する方法
サンプル画面
以下のような画面で検索機能を実装します。

検索フォームに入力した文字が含まれるメンバーが入力フォームの下にある4つの中から残って表示されます。(含まれないメンバーは非表示なる)
検索するためのボタンはなく、入力値が変わるたびに検索結果が変わるのでリアルタイム検索と呼んでいます。
サンプルコード
import React, { useState } from "react";
type member = {
name: string;
country: string;
food: string;
};
type MemberList = Array<member>;
const allMemberList = [
{
name: "太郎",
country: "Japan",
food: "焼肉"
},
{
name: "花子",
country: "Japan",
food: "ケーキ"
},
{
name: "リチャード",
country: "Canada",
food: "ステーキ"
},
{
name: "マイケル",
country: "USA",
food: "ハンバーガー"
}
];
export default function App() {
const [inputValue, setInputValue] = useState("");
const [memberList, setMemberList] = useState<MemberList>(allMemberList);
const search = (value: string) => {
if (value !== "") {
const filteredList = allMemberList.filter((member: member) =>
Object.values(member).some(
(item: string) =>
item?.toUpperCase().indexOf(value.trim().toUpperCase()) !== -1
)
);
setMemberList(filteredList);
return;
}
setMemberList(allMemberList);
return;
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value);
search(e.target.value);
};
return (
<div className="App">
<h1>メンバーリスト</h1>
<div>
<span style={{ marginRight: "5px" }}>検索フォーム</span>
<input type="text" value={inputValue} onChange={handleChange} />
</div>
<ul>
{memberList.map((member, index) => {
return (
<li key={index}>
{member.name} / {member.country} / {member.food}
</li>
);
})}
</ul>
</div>
);
}
検索画面の多くは初期状態では検索対象データが全件表示されているので、それを実現するためにallMemberList
に検索対象データ全件をべた書きしています。(実際はDBのデータを使うことになると思います)
検索機能のコアの部分に絞って解説します。
リアルタイム検索機能
検索機能は名前のとおりsearch()
です。
const search = (value: string) => {
if (value !== "") {
const filteredList = allMemberList.filter((member: member) =>
Object.values(member).some(
(item: string) =>
item?.toUpperCase().indexOf(value.trim().toUpperCase()) !== -1
)
);
setMemberList(filteredList);
return;
}
setMemberList(allMemberList);
return;
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value);
search(e.target.value);
};
search
は検索フォームの入力値変更時に呼ばれるhandleChange
から呼び出されるようにしています。
まず、検索フォームの入力値がない場合は画面表示するメンバーを管理するローカルstate(memberList
)をベタ書きした全てのメンバーデータで更新します。
if (value !== "") {
// 検索処理
}
// 全てのメンバーをローカルstateにセットする
setMemberList(allMemberList);
return;
検索処理は以下のコードです。
const filteredList = allMemberList.filter((member: member) =>
Object.values(member).some(
(item: string) =>
item?.toUpperCase().indexOf(value.trim().toUpperCase()) !== -1
)
);
検索対象の全データ(allMemberList
)をfilter
関数でフィルタリングしています。
filter
は「配列の各要素を引数にとるコールバック関数の返り値がtrueになる要素を返して新しい配列を作成する」関数です。
Array.filter((member: member) => boolean値を返すコールバック関数 );

フィルタリングの内容(上記の「boolean値を返すコールバック関数」)は以下のコードです。
Object.values(member).some(
(item: string) =>
item?.toUpperCase().indexOf(value.trim().toUpperCase()) !== -1
)
some
は「配列の少なくとも一つの要素が、引数にとるコールバック関数で実装されたテストに合格するかどうか(trueを返すか)をテストする」関数です。
some
の返り値はboolean
型です。

Object.values(member)
でオブジェクトmember
のvalueの配列を作成することができます。
filter
関数には以下のallMemberList
の要素が1つずつ入るので、例えば1つ目のmember
は以下のオブジェクトになります。
{
name: "太郎",
country: "Japan",
food: "焼肉"
}
よって、Object.values(member)
は以下の配列になります。
["太郎", "Japan", "焼肉"]
次にsome関数の中の処理(下記コード)を解説します。
(item: string) =>
item?.toUpperCase().indexOf(value.trim().toUpperCase()) !== -1
item
には["太郎", "Japan", "焼肉"]
の要素が1つずつ入るので、最初は"太郎"
です。
item?.toUpperCase()
で大文字にし、検索フォームの入力値(value
)の文字列の両端の空白をtrim()
で削除、こちらもtoUpperCase()
で大文字にしています。
(item: string) =>
item?.indexOf(value.trim()) !== -1
そして、indexOf()
で検索したい文字が検索対象のデータに含まれるかどうかをチェックしています。
indexOf()
は検索したい値が検索対象に含まれない場合は-1を返すので、!== -1
として含まれたらtrue
、含まれない場合はfalse
としています。

検索したい文字(value
)は固定で、["太郎", "Japan", "焼肉"]
の3つの要素をsome()
で1つずつ検証していき、1つでもindexOf()
の結果が-1ではない場合はtrue
を返却します。
そのtrue
をfilter()
が受けとり、以下のオブジェクトを検索後のメンバーデータを格納する配列であるfilteredList
に追加します。
{
name: "太郎",
country: "Japan",
food: "焼肉"
}
少し説明が複雑になってしまいわかりにくいかもしれませんが、このようにすることでフロントエンドでリアルタイム検索を実装することができます。
試しに検索フォームに「japa」と入力すると、「Japan」という情報を持つデータが残っていることがわかります。

CodeSandboxのリンク
冒頭に載せていますが、以下のリンクから実際に検索機能のデモを動かすことができます。

最後に
フロントエンドで完結する検索機能は検索ごとにAPIリクエストを行わないので、ユーザーは使い勝手は良いと思います。
JavaScriptにはmap
、filter
、some
、every
などオブジェクトや配列用に便利なメソッドがたくさん用意されているので処理を簡潔に書くことができて良いですね。
React Hooksの基本的な内容はこちらにまとめています。

TypeScript+Next.js(React)+Laravelのハンズオンはこちらから見れます。

コメント