このブログの技術構成について②投稿検索機能

komosyu
Next.js

目次

  1. はじめに
  2. ソースコード
  3. 実装の手順
    1. 入力テキストの保持
    2. 入力したテキストから投稿の絞り込み
    3. 絞り込んだ投稿を表示する
  4. 参考
  5. さいごに

はじめに

今回はこのブログのsearch ページでの投稿検索機能についての実装方法について説明しようと思います。
このブログでは、データベースを設置してる訳でも headlessCMS を採用している訳でもないので、algolia を採用したり API Routes を採用したりといった難しいすることはなく、単純な仕組みになっています。

ソースコード

このブログの中で、検索機能を実装しているのはこちらのコードになります。

実装の手順

まず、検索ページを作成して検索したいテキストを入力するためのフォームと検索結果の投稿を表示するコンポーネントを用意します。

このブログの場合は SearchMain というコンポーネントとして用意しました。

仕組みとしては非常にシンプルなものです。

コンポーネントの内容はこんな感じです。

import { useState } from 'react'
import style from 'styles/SearchMain.module.scss'
import { Posts } from 'type'
import PostsList from './PostsList'

const SearchMain = ({ posts }: Posts) => {
  const [searchValue, setSearchValue] = useState('')
  let filteredPosts = posts.filter(
    (post) =>
      post.frontMatter.title
        .toLowerCase()
        .includes(searchValue.toLowerCase()) ||
      post.frontMatter.description
        .toLowerCase()
        .includes(searchValue.toLowerCase()) ||
      post.frontMatter.publishedDate
        .toLowerCase()
        .includes(searchValue.toLowerCase()) ||
      post.frontMatter.modifiedDate
        .toLowerCase()
        .includes(searchValue.toLowerCase()) ||
      post.frontMatter.category
        .toLowerCase()
        .includes(searchValue.toLowerCase())
  )

  return (
    <div className={style.container}>
      <div className={style.form}>
        <input
          className={style.formInput}
          onChange={(e) => setSearchValue(e.target.value)}
          type="text"
          placeholder="記事を検索する"
        />
        <svg
          className={style.formIcon}
          width="29"
          height="27"
          viewBox="0 0 29 27"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
        >
          <circle cx="10" cy="10" r="9" stroke="white" strokeWidth="2" />
          <line
            x1="17.6827"
            y1="16.2693"
            x2="27.9123"
            y2="25.8273"
            stroke="white"
            strokeWidth="2"
          />
        </svg>
      </div>
      {filteredPosts.length > 0 && <PostsList posts={filteredPosts} />}
    </div>
  )
}

export default SearchMain

それでは、ここから詳細な解説をしていこうと思います。

入力テキストの保持

useState で input フォームに入力した内容を searchValue に保持しておきます。

const [searchValue, setSearchValue] = useState('')
return (
  <input
    className={style.formInput}
    onChange={(e) => setSearchValue(e.target.value)}
    type="text"
    placeholder="記事を検索する"
  />
)

入力したテキストから投稿の絞り込み

すべての投稿の中にある title や description の中に、フォームに入力したテキストと一致する投稿を filter メソッドで絞り込みます。

let filteredPosts = posts.filter(
  (post) =>
    post.frontMatter.title.toLowerCase().includes(searchValue.toLowerCase()) ||
    post.frontMatter.description
      .toLowerCase()
      .includes(searchValue.toLowerCase()) ||
    post.frontMatter.publishedDate
      .toLowerCase()
      .includes(searchValue.toLowerCase()) ||
    post.frontMatter.modifiedDate
      .toLowerCase()
      .includes(searchValue.toLowerCase()) ||
    post.frontMatter.category.toLowerCase().includes(searchValue.toLowerCase())
)

絞り込んだ投稿を表示する

filteredPosts として、絞り込まれた投稿のみを PostsList コンポーネントに渡して、表示させて完成です。

return filteredPosts.length > 0 && <PostsList posts={filteredPosts} />

参考

vercel 社の中の人である、leerob さんがのブログを見て、検索機能どうやっているのかな?と思って公開されているソースコードを参考にさせていただきました。(ほぼそのままですね w すみません。。)

さいごに

ブログを作るときに検索機能を絶対つけたいけど、難しそうだなー。なんて思っていましたが、今回みたいなデータベースや headlessCMS が絡んでいない場合はあっさりできてしまいますね。