Udemyのオススメ講座はこちら 詳細を見てみる

【React】フロントエンドでのリアルタイム検索を実装

  • URLをコピーしました!

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

「フロントエンドで完結する」と書きましたが、検索対象のデータをAPIから取得する場合は厳密にはフロントエンドで完結していないようにも捉えられますが、検索時にAPIへのリクエストを行わないという意味で認識してください。

この記事で実装するサンプルのデモはCodeSandoxで触ることができます。

目次

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()で大文字にしています。

2つの値を両方大文字にすることは日本語では意味がありませんが、英語の検索でJapanとjapanを同じものとして検索に引っかかるようにするためにしています。なので大文字と小文字も厳密に検証する場合は以下のように大文字に変換する処理はなくても良いです。

 (item: string) =>
   item?.indexOf(value.trim()) !== -1

そして、indexOf()で検索したい文字が検索対象のデータに含まれるかどうかをチェックしています。

indexOf()検索したい値が検索対象に含まれない場合は-1を返すので、!== -1として含まれたらtrue、含まれない場合はfalseとしています。

検索したい文字(value)は固定で、["太郎", "Japan", "焼肉"]の3つの要素をsome()で1つずつ検証していき、1つでもindexOf()の結果が-1ではない場合はtrueを返却します。

そのtruefilter()が受けとり、以下のオブジェクトを検索後のメンバーデータを格納する配列であるfilteredListに追加します。

  {
    name: "太郎",
    country: "Japan",
    food: "焼肉"
  }

少し説明が複雑になってしまいわかりにくいかもしれませんが、このようにすることでフロントエンドでリアルタイム検索を実装することができます。

試しに検索フォームに「japa」と入力すると、「Japan」という情報を持つデータが残っていることがわかります。

検索結果

CodeSandboxのリンク

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

最後に

フロントエンドで完結する検索機能は検索ごとにAPIリクエストを行わないので、ユーザーは使い勝手は良いと思います。

JavaScriptにはmapfiltersomeeveryなどオブジェクトや配列用に便利なメソッドがたくさん用意されているので処理を簡潔に書くことができて良いですね。

React Hooksの基本的な内容はこちらにまとめています。

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

Udemyのオススメ講座はこちら↓

TypeScript+Next.js+Laravelハンズオンはこちら↓

デスク周りのオススメアイテムはこちら↓

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

ブログを開設するなら「SWELL」が絶対オススメ!

この記事を書いた人

大学院(機械工学)→重工業→エンジニア→プロダクトマネージャー(PdM)兼エンジニア

神戸で「つながる勉強会」を運営↓
https://tsunagaru-kobe.connpass.com/

神戸グルメのインスタアカウントを運用しています。

目次