ホームGatsby【連載】Gatsbyブログのデザインをワードプレスちっくにする手順(その9)サイドバーに月別アーカイブをつけました。
2020年6月11日

【連載】Gatsbyブログのデザインをワードプレスちっくにする手順(その9)サイドバーに月別アーカイブをつけました。

calendar

10月28日追記
Gatsbyブログについて、初noteを書きました。
【完全版】爆速GatsbyでWordPressちっくなブログを作る全手順

こんにちは、筋肉めがねです。

Gatsbyブログのサイドバーに月別アーカイブをつけましたので、そのプロセスをシェアします。

月別アーカイブの良いところは、ブログを書いている人のモチベーションの推移がパッと分かる事ですよね。 僕の場合は、ブログを開始した1月は2日に1記事アップできるペースでしたが、3月、4月と年度をまたぐ時期にペースが落ちています。
6月からは挽回していきたいですね。

月別アーカイブを作る上で参考にさせていただいた記事は以下です。大変感謝です。
Gatsby.jsで年ごと、月ごとで記事一覧を表示したい

月別のページを作成

まずは、gatsby-node.jsにて、月別のページを作ります。

gatsby-node.js
const _ = require('lodash')
...

exports.createPages = ({ graphql, actions }) => {

  const { createPage } = actions

  return new Promise((resolve, reject) => {
    ...
    const periodTemplate = path.resolve('./src/templates/period-template.jsx') //後ほどtemplateを作成します。

    graphql(`
      {
        allMarkdownRemark(
          limit: 1000
          filter: { frontmatter: { draft: { ne: true } } , fields: { draft: { eq: false } } }
          sort: { order: DESC, fields: [frontmatter___date] }
        ) {
          edges {
            node {
              fields {
                slug
              }
              frontmatter {
                title
                tags
                layout
                category
                year: date(formatString: "YYYY") //Yearの情報を取得します。
                month: date(formatString: "MM") //Monthの情報を取得します。
              }
            }
          }
        }
      }
    `).then(result => {
      if (result.errors) {
        console.log(result.errors)
        reject(result.errors)
      }

      const posts = result.data.allMarkdownRemark.edges

      let postsBlog = []

      const years = new Set();
      const yearMonths = new Set();
  
      posts.forEach((post) => {
        
        if(post.node.fields.slug.includes('/posts')){
          postsBlog.push(post)

          const { year, month } = post.node.frontmatter;

          years.add(year);
          yearMonths.add(`${year}/${month}`);

        } else{

        }
      })

      // 月別ページ
      yearMonths.forEach(yearMonth => {
        const [year, month] = yearMonth.split('/')
        const startDate = `${year}-${month}-01T00:00:00.000Z`;
        const newStartDate = new Date(startDate);
        // 月末日を取得
        const endDate = new Date(
          new Date(newStartDate.setMonth(newStartDate.getMonth() + 1)).getTime() -
            1
        ).toISOString();
  
        createPage({
          path: `/${year}/${month}/`,
          component: periodTemplate,
          context: {
            displayYear: year, //displayYearを次のtemplateで使います。
            displayMonth: month, //displayMonthも次のtemplateで使います。
            periodStartDate: startDate,
            periodEndDate: endDate
          }
        });
      });
      ...

これで、gatsby build時に月毎のページが作られます。 例えば、以下のようなページが作られます。

Terminal
https://kinnikumegane.com/2020/06

もちろん、period-template.jsxを作成していないので、上のコードだけでは動きません。

period-template.jsxの作成

続いて、period-template.jsxを作成します。gatsby-node.jsにて読み込むテンプレートですね。

/src/templates/period-template.jsx
import React from 'react'
import Helmet from 'react-helmet'
import { graphql } from 'gatsby'
import Layout from '../components/Layout'
import Sidebar from '../components/Sidebar'
import PeriodTemplateDetails from '../components/PeriodTemplateDetails'

class PeriodTemplate extends React.Component {
  render() {
    const { title } = this.props.data.site.siteMetadata

    //gatsby-node.jsで設定したぢdisplayMonth, displayYearを読み込みます。
    const { displayMonth, displayYear } = this.props.pageContext

    return (
      <Layout>
        <div>
          <Helmet title={`${displayYear}${displayMonth}月の記事 - ${title}`} />
          {/* commented out by kinniku <Sidebar {...this.props} /> */}
          <PeriodTemplateDetails {...this.props} /> //PeriodTemplateDetails componentはこの次に作成します。
          <Sidebar {...this.props} />
        </div>
      </Layout>
    )
  }
}

export default PeriodTemplate

export const pageQuery = graphql`
  query PeriodPage($periodStartDate: Date, $periodEndDate: Date) {
    site {
      siteMetadata {
        title
        subtitle
        copyright
        menu {
          label
          path
        }
        author {
          name
          twitter
          github
        }
      }
    }
    allMarkdownRemark(
      limit: 1000
      filter: {
        frontmatter: {
          date: { gte: $periodStartDate, lt: $periodEndDate }
          layout: { eq: "post" }
          draft: { ne: true }
        }
      }
      sort: { order: DESC, fields: [frontmatter___date] }
    ) {
      edges {
        node {
          fields {
            slug
            tagSlugs
            categorySlug
          }
          frontmatter {
            title
            featuredImage {
              childImageSharp {
                sizes(maxWidth: 630) {
                  ...GatsbyImageSharpSizes
                }
              }
            }
            tags
            date
            category
            description
            year: date(formatString: "YYYY")
            month: date(formatString: "MM")
          }
        }
      }
    }
  }
`

PeriodTemplateDetails componentの作成

どんどん行きましょう。

続いて、PeriodTemplateDetails componentを作成します。
1つ前のperiod-template.jsxにて読み込まれるcomponentです。

/src/components/PeriodTemplateDetails/index.jsx
import React from 'react'
import Post from '../Post'
import './style.scss'

class PeriodTemplateDetails extends React.Component {
  render() {
    const items = []
    
    const { displayMonth, displayYear } = this.props.pageContext;

    const posts = this.props.data.allMarkdownRemark.edges
    posts.forEach(post => {
      items.push(<Post data={post} key={post.node.fields.slug} />)
    })

    return (
      <div className="content">
        <div className="content__inner">
          <div className="page">
            <h1 className="page__title" id="period_title_area">
              <div className="period_title">
                <span className="periodMark"></span>
                &quot;
                {displayYear}{displayMonth}月の記事
                &quot;
              </div>
              <div className="number_of_post_periodpage">{items.length}件の記事があります</div>
            </h1>
            <div className="page__body">{items}</div>
          </div>
        </div>
      </div>
    )
  }
}

export default PeriodTemplateDetails

ここまでで、 一旦gatsby developし、例えば、localhost:8000/2020/01にアクセスすると、以下のように月ごとのページが表示されます。

monthly page

PeriodList componentの作成

続いて、月別アーカイブ(PeriodList)のcomponentを作ります。

/src/
// src/components/category-list.js
import React from "react"
import kebabCase from 'lodash/kebabCase'
import './style.scss'

// Components
import { Link, StaticQuery, graphql } from "gatsby"

const PeriodList = () => (
  <StaticQuery
    query={graphql`
      query {
        allMarkdownRemark(limit: 2000) {
          group(field: frontmatter___date) {
            fieldValue
            totalCount
          }
        }
      }
    `}
    render={data => {    
      const monthGrouppedData = data.allMarkdownRemark.group;
      let counts = []
      let monthValue = []

      for(let i = 0; i< monthGrouppedData.length; i++){

        let monthGroupdDateFieldValue = new Date(monthGrouppedData[i].fieldValue)
        monthValue.push(monthGroupdDateFieldValue.getMonth());
        
      }

      monthValue.forEach(function(x) { 
        counts[x] = (counts[x] || 0)+1; 
      });

      let counts2 = []

      for(let i = 0; i < counts.length; i++){
        counts2.push({
          number: counts[i],
          index: ('0' + (i+1)).slice(-2),
        })
      }

      let reverseCounts = counts2.reverse();

      // return JSX
      return (
        <nav>
          <h1 className="periodlist-header">月別アーカイブ</h1>
          <ul className="period-list">         
            {reverseCounts.map(date => (
              <li className="period-list-item" key={date.index}>
                <span className="period-list-icon"></span>
                <Link className="period-list-item-link" to={`/2020/${kebabCase(date.index)}/`}>
                  2020年{date.index}月({date.number})
                </Link>
              </li>
            ))}
          </ul>
        </nav>
      )
    }}
  />
)

export default PeriodList

一旦2020をハードコーディングしてますが、ここを変数にできるよう2021年までは必ずブログを続けます。

data.allMarkdownRemark.groupから、年、月のリストを作る方法は幾つかあると思います。 上のコードよりも断然スマートな実装方法がありましたらご連絡いただけますと助かります。

SidebarでPeriodListを読み込む

最後です。

SidebarにてPeriodList componentを読み込みます。

/src/components/Sidebar/index.jsx
...
return (
  <div className="sidebar">
    <div className="sidebar__inner">
      <Adsense />
      <div className="sidebar__author">{authorBlock}</div>
      <div>
        <Menu data={menu} />
        ...
        <Links data={author} />
        <CategoryList />

        <PeriodList />
        <Adsense />
      </div>
    </div>
    ...

まとめ

これで、サイドバーに月別アーカイブが表示されました。

シェアする