Gatsbyで作ったブログに関連記事を表示する

2020/01/02

あけましておめでとうございます。たまおさ(@tamaki_osamu)です。

初詣、初夢、初日の出。新年は何かと"初"がつきものですよね。今年僕がやった"初"のことは、このブログに「関連記事」を表示する機能を追加することでした。

関連記事を表示する

各記事の終わりに関連記事を表示することを目指します(このブログはGatsbyJSで作成しています)。

関連記事のロジック

今回は簡単に済ませることにしました。

このブログには各記事に「タグ」をつけています。これを利用して、各記事と同様のタグを含む記事を日付が新しい順に関連記事として表示させることにしました。

ただ、このロジックでは古い記事がなかなか関連記事として表示されず埋もれてしまいます。将来的に記事数が増えてきたら考え直す必要がありそうです。

関連記事のコンポーネント

こちらの記事が大変参考になりました。ありがとうございます。

どうやら、StaticQueryを用いて一度全記事を取得してから、関連記事に絞りこむようなコンポーネントを書けばうまくいくようです。

そこで次のようなコンポーネントを作ってみました(ほとんどコピペですが...)。

import React from "react";
import PropTypes from "prop-types";
import { Link, graphql, StaticQuery } from "gatsby";
import PreviewCompatibleImage from "./PreviewCompatibleImage";

const RelatedList = ({ title, tags }) => (
  <StaticQuery
    query={graphql`
      query RelatedListQuery {
        allMarkdownRemark(
          sort: { order: DESC, fields: [frontmatter___date] }
          filter: { frontmatter: { templateKey: { eq: "blog-post" } } }
        ) {
          edges {
            node {
              id
              fields {
                slug
              }
              excerpt(format: PLAIN, pruneLength: 75, truncate: true)
              frontmatter {
                title
                tags
                date(formatString: "YYYY/MM/DD")
                featuredimage {
                  childImageSharp {
                    fluid(maxWidth: 240, quality: 100) {
                      ...GatsbyImageSharpFluid
                    }
                  }
                }
              }
            }
          }
        }
      }
    `}
    render={data => {
      const relatedPosts = data.allMarkdownRemark.edges.filter(edge => {
        if (edge.node.frontmatter.title === title) {
          return false;
        }
        for (let i = 0; i < tags.length; i++) {
          if (edge.node.frontmatter.tags.indexOf(tags[i]) >= 0) {
            return true;
          }
        }
        return false;
      });
      if (!relatedPosts) {
        return null;
      }
      const relatedPost4 = relatedPosts.slice(0, 4);

      return (
        <div className="is-multiline">
          {relatedPost4 &&
            relatedPost4.map(({ node: post }) => (
              <div className="is-parent is-6" key={post.id}>
                <Link to={post.fields.slug}>
                  <article
                    className={`blog-list-item tile is-child box notification `}
                  >
                    <div className="flex">
                      <div className="featured-thumbnail">
                        <PreviewCompatibleImage
                          imageInfo={{
                            image: post.frontmatter.featuredimage,
                            alt: `featured image thumbnail for post ${post.title}`
                          }}
                        />
                      </div>
                      <p className="post-meta">
                        <span />
                        <span className="title is-size-5">
                          {post.frontmatter.title}
                        </span>
                        <span />
                        <span className="is-size-6 is-block">
                          ({post.frontmatter.date})
                        </span>
                        <span className="is-size-6 is-block">
                          {post.excerpt}
                        </span>
                      </p>
                    </div>
                  </article>
                </Link>
              </div>
            ))}
        </div>
      );
    }}
  />
);

export default RelatedList;

RelatedList.propTypes = {
  data: PropTypes.shape({
    allMarkdownRemark: PropTypes.shape({
      edges: PropTypes.array
    })
  })
};

Propsとして親記事のタイトルとタグを渡しています。 StatiQuery で全記事を取得した後、filterで親記事のタグを含む記事のみを抽出しています。その後、抽出してきた配列のうち最初の 4 つを関連記事として表示するようにしています。

いい感じに関連記事を表示することができました。

最後に

いまのところ、悲しいことに関連記事を表示するまでもない記事量です...今年度は関連記事が埋まるようアウトプットがんばります()

logo

たまおさ

釣りとか登山とか好きです。(@tamaki_osamu)