Gatsbyブログに爆速な検索機能を追加

2021.05.12

ブログに検索機能がほしい

まだあまり記事数が多くないが、やはりブログに検索機能がほしいと思い、Gatsbyで実装できるものを調査。

Gatsby公式ページにも検索機能の追加についての説明がある。

メジャーなのは、SaaSであるAlgoliaを使うもの。 ただAlgoliaは有料なので個人的にオーバースペックかと思い、ローカルで実装できるものを探す。

検索機能の追加

以下のページを参考に、GatsbyでMarkdownファイルを入稿する際に読み込む記事のタイトルや概要を対象に検索できる機能を追加する。

検索はモーダルウィンドウで実行するようにするため、react-modalを追加する。

yarn add react-modal

あとは、検索用のコンポーネントsearch.jsと、モーダル表示用のコンポーネントmodal-search.jsを作成。

src/components/search.js
import React, { useState } from "react"
import { Link, useStaticQuery, graphql } from "gatsby"

const SearchResult = () => {
   const data = useStaticQuery(graphql`
   query {
     allMarkdownRemark(sort: { order: DESC, fields: frontmatter___date }) {
   	edges {
   	  node {
   		excerpt(pruneLength: 200)
   		id
   		frontmatter {
   		  title
   		  description
   		  date(formatString: "YYYY.MM.DD")
   		  tags
   		}
   		fields {
   		  slug
   		}
   	  }
   	}
     }
   }
 `)

 const allPosts = data.allMarkdownRemark.edges
 const emptyQuery = ""

 const [state, setState] = useState({
   filteredData: [],
   query: emptyQuery,
 })

 const handleInputChange = event => {
   console.log(event.target.value)
   const query = event.target.value
   const posts = data.allMarkdownRemark.edges || []

   const filteredData = posts.filter(post => {
     const { description, title } = post.node.frontmatter
     return (
       description.toLowerCase().includes(query.toLowerCase()) ||
       title.toLowerCase().includes(query.toLowerCase())
     )
   })

   setState({
     query,
     filteredData,
   })
 }

 const { filteredData, query } = state
 const hasSearchResults = filteredData && query !== emptyQuery
 const posts = hasSearchResults ? filteredData : allPosts

   return (
   	<div>
   		<div className="result-inner">
   			<input
   				type="text"
   				aria-label="Search"
   				placeholder="検索ワードを入力..."
   				onChange={handleInputChange}
   			/>
   			<div className="result-inner__res">
   				{query !== "" ?
   					query + " の検索結果: " + posts.length + "件"
   					: posts.length + "件の記事があります"
   				}
   			</div>
   			<ul className="result-inner__search">
   			  {query !== "" ?
   			     posts.map(({ node }) => {
                      const { slug } = node.fields
   				   const { title } = node.frontmatter
   				   return (
   						<li key={slug}>
   							<Link to={slug}>{title}</Link>
   						</li>
   					)})
   				  : ""
   			  } 
   			</ul>
   		</div>
   	</div>
   )
}

export default SearchResult
src/components/modal-search.js
import React from 'react';
import Modal from 'react-modal';
import Search from "./search";
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSearch } from "@fortawesome/free-solid-svg-icons";

Modal.setAppElement('#___gatsby')
class ModalWindow extends React.Component {
  constructor() {
    super();
    this.state = {
      modalIsOpen: false
    };
    this.openModal = this.openModal.bind(this);
    this.closeModal = this.closeModal.bind(this);
  }
  openModal() {
    this.setState({modalIsOpen: true});
  }
  closeModal() {
    this.setState({modalIsOpen: false});
  }
  render() {
    return (
      <div>
        <FontAwesomeIcon icon={faSearch} onClick={this.openModal} />
        <Modal
          isOpen={this.state.modalIsOpen}
          onRequestClose={this.closeModal}
          contentLabel="Seach Modal"
          className="modalSearchWindow"
          overlayClassName="modalSearchOverlay"
        >
          <Search />
          <button onClick={this.closeModal}>閉じる</button>
        </Modal>
      </div>
    );
  }
}
export default ModalWindow;

あとは、検索アイコンを入れたい場所に<ModalSeach />を入れれば表示される。 検索用のアイコンはfontawesomeを使用。

検索フォームに文字を入力する都度、タイトルと概要を検索し、該当するタイトルを表示する。 検索対象は、本文やタグを追加することもできる。


Recent Posts