22
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【TypeScript+Next.js】 React Hooksで簡易的な検索機能を実装する

Last updated at Posted at 2022-03-20

はじめに

今回はReact HooksのuseStateとuseEffectを使って簡易的な検索機能を紹介します。
正直ライブラリを使ったら楽なんですが、useStateやuseEffectの使い方に慣れるための自己学習用としてご覧いただけたら幸いです。

環境
Next.js
React
Tailwindcss
TypeScirpt

完成した時のデモ画像がこちらです。

全体コードの確認

Card.tsx
import Image from 'next/image';
import React from 'react';

interface ImageProps {
  src: StaticImageData;
  width: number;
  height: number;
  alt: string;
}

export interface CardProps {
  image: ImageProps;
  title: string;
  text: string;
  button: {
    text: string;
    url: string;
  };
}

export const Card: React.FC<CardProps> = ({ image, title, text, button }) => {
  return (
    <div className='w-[300px] text-center rounded-lg shadow-lg '>
      <Image
        src={image.src}
        height={image.height}
        width={image.width}
        alt={image.alt}
        className=' rounded-t-lg'
      />
      <h2 className='my-2 text-xl font-bold'>{title}</h2>
      <p className='px-4 text-sm text-left'>{text}</p>
      <button className='py-1.5 px-4 my-4 text-sm  bg-yellow-500 rounded-full shadow-md'>
        {button.text}
      </button>
    </div>
  );
};

Sreach.tsx
import { useState, useEffect } from 'react';
import { Card, CardProps } from '@/component/Card';
import ProImage from '~/img/profile.png';
const data: CardProps[] = [
  {
    image: {
      src: ProImage,
      width: 300,
      height: 210,
      alt: 'profile',
    },
    title: 'JavaScript',
    text: 'テキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト',
    button: {
      text: 'Read More',
      url: 'https://google.com',
    },
  },
  {
    image: {
      src: ProImage,
      width: 300,
      height: 210,
      alt: 'profile',
    },
    title: 'React',
    text: 'テキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト',
    button: {
      text: 'Read More',
      url: 'https://google.com',
    },
  },
  {
    image: {
      src: ProImage,
      width: 300,
      height: 210,
      alt: 'profile',
    },
    title: 'TypeScript',
    text: 'テキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト',
    button: {
      text: 'Read More',
      url: 'https://google.com',
    },
  },
  {
    image: {
      src: ProImage,
      width: 300,
      height: 210,
      alt: 'profile',
    },
    title: 'Next.js',
    text: 'テキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト',
    button: {
      text: 'Read More',
      url: 'https://google.com',
    },
  },
];
export const Search: React.FC = () => {
  const [showItems, setShowItems] = useState<CardProps[]>([]);

  useEffect(() => {
    setShowItems(data);
  }, []);

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const result = data.filter((item) => {
      return item.title.toLowerCase().match(e.target.value.toLowerCase());
    });
    console.log(result);
    setShowItems(result);
  };

  return (
    <div>
      <form action='' className='flex justify-center'>
        <input
          type='text'
          className='my-8  rounded border border-black'
          placeholder='search'
          onChange={(e) => handleChange(e)}
        />
      </form>
      <div className='flex justify-between'>
        {showItems.map((item, i) => {
          return (
            <>
              <Card image={item.image} title={item.title} text={item.text} button={item.button} />
            </>
          );
        })}
      </div>
    </div>
  );
};

ts.index.tsx
import type { NextPage } from 'next';
import { Search } from '@/component/Search';

const Home: NextPage = () => {
  return (
    <div className='font-bold'>
      <Search />
    </div>
  );
};

export default Home;

Cart.tsxとindex.tsxのコードは省略させていただきます🙇‍♂️

検索をするためには?

検索をする時はまず文字を入力する必要があります。今回は入力した値が含まれているものを表示させるようにすればいいだけです。inputタグのtypeをtextと指定します。

<form action='' className='flex justify-center'>
  <input
  type='text'
  className='my-8  rounded border border-black'
  placeholder='search'
  onChange={(e) => handleChange(e)}
  />
</form>

あとは入力された際のビジネスロジックを書くのとデータを用意すればOKです。Reactで用意されているフォームイベントのonChangeの中にロジックを記入します。

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const [showItems, setShowItems] = useState<CardProps[]>([]);
    
    useEffect(() => {
    setShowItems(data);
    }, []);
    ...

    const result = data.filter((item) => {
      return item.title.toLowerCase().match(e.target.value.toLowerCase());
    });
    setShowItems(result);
  };

フォームの中に入力されるたびにstate変数のshowItemsを更新していくようにします。今回はカードコンポーネントのタイトルを基準にしていますが、テキストを基準にすることもできます。その時は item.title.toLowerCase().match(e.target.value.toLowerCase());の中のtitleをtextに変更してください。
補足にはなりますが、.toLowerCase()にしているのは大文字から小文字に直している処理です

入力した検索結果をstate変数に代入をしていき、入力したときと入力した文字を削除するときに更新されていきます。 
あとはカードコンポーネントの中にデータを入れていけば完成です!

useEffectを使っているのは、ページがレンダリングされる前にstate変数にデータを代入させるために使ってます。

余談
私自身ビジネスロジックのコードを書いてる途中で手が止まることがあります。その時は入力するデータと出力されるデータの入口と出口を決めてコードを書くように心がけてます。例えば、stringからnumberの値が欲しいだったり、インデックス番号2の配列からstringが欲しいみたいに考えたら案外スッキリとコードが書けたりできるのでぜひ試してみてください!

最後に

実装してみると案外呆気ないものでした😄笑
React Hooksの練習にもなるので実際に手を動かして試してみてください!
最後まで読んでいただきありがとうございました!

22
18
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?