mottox2 blog

blog

GatsbyJS v2 はじめの一歩 (7) 動的にページを生成する

gatsby
mottox2
mottox2
2018/09/09

今までの記事
GatsbyJS v2 はじめの一歩 (1) 開発環境の立ち上げ
GatsbyJS v2 はじめの一歩 (2) 実際にページを作る
GatsbyJS v2 はじめの一歩 (3) ページにスタイルを当てる
GatsbyJS v2 はじめの一歩 (4) gatsby buildとデプロイ
GatsbyJS v2 はじめの一歩 (5) GraphQLによるデータ管理
GatsbyJS v2 はじめの一歩 (6) Source Pluginの利用


前回の記事ではSource Pluginを使って外部のデータをReactコンポーネントで使えるようにしました。ただし、前回のままだと一覧ページしか作っていません。ブログなどのウェブサイトを作ろうとすると、一覧ページだけではなく詳細ページも欲しくなると思います。

GatsbyJSのルーティングはsrc/pages以下のルーティングで決定されるということは以前説明しましたが、これでは外部データに依存する(例えば、記事詳細ページ)を作ることが出来ません。そこでgatsby-node.jsを利用することでGraphQLのデータを元にページを生成する方法も用意されています。

gatsby-node.jsはGatsbyJSの動作をカスタマイズするファイルです。このファイルの中でGatsbyJSのライフサイクルに合わせた関数をexportすることで希望の動作をさせることが出来ます。参考: Node APIs
今回はcreatePagesを使用します。この関数は動的にページを生成するための関数でプラグインによるソースの追加・加工が終わったタイミングで実行されます。

gatsby-node.js
exports.createPages = ({ graphql, actions }) => {
  const { createPage } = actions
  const postTemplate = path.resolve(`./src/templates/PostTemplate.jsx`)
  return new Promise((resolve, reject) => {
    graphql(`
      allHnStory {
        edges {
          node {
            id
            title
            score
            order
            domain
            url
          }
        }
    }
    `).then(result => {
      // GraphQLのデータを使ってページを追加する処理
      result.data.allHnStory.edges.forEach(edge => {
        const node = edge.node
        createPage({
          path: `/posts/${node.id}`,
          component: postTemplate
        })
      })
      resolve()
    })
  })
}

それでは順を追ってコードの解説をしていきます。

動的に追加するには次の手順を行います

  1. GraphQLでデータを取得する
  2. 動的に作成するページのテンプレートを作成する
  3. 取得したデータを作ったテンプレートに適用する

まずはGraphQLでデータを取得します。次のコードをgatsby-node.jsに追加してください。

js
exports.createPages = ({ graphql }) => {
  return new Promise((resolve, reject) => {
    graphql(`
      {
        allHnStory {
          edges {
            node {
              id
              title
              score
              order
              domain
              url
            }
          }
        }
      }
    `).then(result => {
      console.log(JSON.stringify(result, null, 4))
      resolve()
    })
  })
}

このコードを保存して、gatsby developを実行するとビルド中に以下の出力が表示されます。無事にデータが取得されconsole.logで表示されました。

Screen Shot 2018-09-08 at 16.19.38.png (222.2 kB)

次に動的に作成するページのテンプレートを作成します。Gatsbyでは動的に生成されるファイルはsrc/templates/以下に保存することが通例となっています。
今回は src/templates/story.jsx として次のファイルを追加します。

import React from "react"

export default () => {
  return (
    <div>This is story page</div>
  )
}

最後にGraphQLで取得したデータと、作成したテンプレートを紐づけます。ページを作成するためのメソッドがactions.createPageです。path(サイト上のパス)とcomponent(適用したいテンプレートのファイルパス)とcontext(ページに渡したい変数のオブジェクト)を引数とするメソッドです。

const path = require('path')

exports.createPages = ({ graphql, actions }) => {
  const { createPage } = actions
  return new Promise((resolve, reject) => {
    graphql(`
      {
        allHnStory {
          edges {
            node {
              id
              title
              score
              order
              domain
              url
            }
          }
        }
      }
    `).then(result => {
      result.data.allHnStory.forEach(storyEdge => {
        const node = storyEdge.node
        const path = `/stories/${node.id}`
        console.log(path)
        createPage({
          path: path
          component: path.resolve(`./src/templates/story.js`),
          context: {
            id: node.id
          }
        })
      } )
      resolve()
    })
  })
}

この状態で再度gatsby devevelopを実行しましょう。実行時のログにpathを表示しているので、それをURL欄に入力すると、Templateで定義したページが表示されます。

TODO: スクショ

この状態ではまだ動的にURLに対応するページが生成されただけで、ページの内容にデータが反映されていません。ページ内にデータを反映させていきましょう。src/template/story.jsxを次のように編集します。
graphQLでeq: $idとあるのは、createPageで渡したcontext内にあるidです。idと一致するhnStoryを取得するというクエリを投げています。この状態でブラウザを開くと、個別のデータが入ったページが表示されます。

import React from "react"
import { graphql } from "gatsby"

export default ({ data }) => {
  const story = data.hnStory
  return (
  <div>  
    <h1>{story.title}</h1>      
  </div>
  )
}

export const query = graphql`
  query($id: String!) {
    hnStory(fields: { id: { eq: $id } }) {
              id
              title
              score
              order
              domain
              url
    }
  }
`

このような手順で、動的にページを作成していくことができます。多少長い手順となりましたが、データとテンプレートの結合度が低い状態で実装していけることが分かると思います。

@mottox2フリーランスWebデベロッパー

都内でフリーランスエンジニア・デザイナーとしてWebサービスやスマホアプリを作っています。Ruby on Railsでの新規事業の爆速立ち上げや、使いやすいSPAの開発が得意です。

お問い合わせはこちら