Fetching remote data

Gatsby has a rich ecosystem of data source plugins—we don't have time go through all of them. It's common for a Gatsby source plugin to reach out to another system and fetch data over the network at build time. The gatsby-source-hacker-news plugin is a great plugin to start with, so that you can see how this fetching process works with Gatsby.

Instead of building your own Hacker News website using Gatsby, we'll use the demo created by https://github.com/ajayns. To get started, you can clone into his repo as follows:

git clone https://github.com/ajayns/gatsby-hacker-news.git
cd gatsby-hacker-news

Then you can install dependencies, including the gatsby-source-hacker-news plugin:

npm install

You don't need to edit the project configuration to enable anything, because this is already a Gatsby project. Simply start the development server as you've done throughout this chapter:

gatsby develop

Compared to other websites you've worked on this chapter, this time around the build takes longer to complete. This is due to the fact that that Gatsby has to fetch data over the network. There are also more resources to fetch. If you look at the console output from the development server, you should see the following:

success onPreBootstrap - 0.011 s
 starting to fetch data from the Hacker News GraphQL API. Warning, this can take a long time e.g. 10-20 seconds
 source and transform nodesfetch HN data: 10138.119ms

This indicates that the build will take longer due to the work that needs to happen in order to load the Hacker News data. Once this process completes, you can load the site in your browser. You should see something similar to the following:

Let's take a look at the GraphQL query that loaded the data used to render this content. In the index.js page, you'll find the following query:

query PageQuery { 
  allHnStory(sort: { fields: [order] }, limit: 10) { 
    edges { 
      node { 
        ...Story 
      } 
    } 
  } 
} 

Instead of individual node fields being specified, there's ...Story. This is called a fragment and it's defined in the StoryItem component:

fragment Story on HNStory { 
  id 
  title 
  score 
  order 
  domain 
  url 
  by 
  descendants 
  timeISO(fromNow: true) 
} 

The StoryItem component defines this GraphQL fragment because it uses this data. Now, let's shift over to GraphiQL and put this query together and execute it:

This is how the home page of the site loads data fetched from the Hack News API. Here's what the home page component looks like:

import React from 'react' 
 
import StoryItem from '../components/story-item' 
 
const IndexPage = ({ data, active }) => ( 
  <div> 
    <div> 
      {data.allHnStory.edges.map(({ node }) => ( 
        <StoryItem key={node.id} story={node} active={false} /> 
      ))} 
    </div> 
  </div> 
) 
 
export default IndexPage 

The edges of the returned data are mapped to StoryItem components, passing in the data node. Here's what the StoryItem component looks like:

import React, { Component } from 'react'; 
import Link from 'gatsby-link'; 
 
import './story-item.css'; 
 
const StoryItem = ({ story, active }) => ( 
  <div 
    className="story" 
    style={active ? { borderLeft: '6px solid #ff6600' } : {}} 
  > 
    <div className="header"> 
      <a href={story.url}> 
        <h4>{story.title}</h4> 
      </a> 
      <span className="story-domain"> 
        {' '}({story.domain}) 
      </span> 
    </div> 
    <div className="info"> 
      <h4 className="score">▴ {story.score}</h4> 
      {' '} 
      by <span className="author">{story.by}</span> 
      {' '} 
      <span className="time">{story.timeISO}</span> 
      {' '} 
      {active ? ( 
        '' 
      ) : ( 
        <Link to={'/item/${story.id}'} className="comments"> 
          {story.descendants} comments 
        </Link> 
      )} 
    </div> 
  </div> 
); 
 
export default StoryItem; 

Here you can see how this component uses the data defined by the GraphQL fragment that was passed to the larger query.

Now let's click on the comments link of a story, which will take you to the details page of a story. The new URL should look something like http://localhost:8000/item/16691203 and the page should look something like this:

You're probably wondering where this page comes from, given that it has a URL parameter (the ID of the story). When using Gatsby to build static pages that have a dynamic URL component to them, you have to write some code whose job is to tell Gatsby how to create pages based on GraphQL query results. This code goes into the gatsby-node.js module. Here's how the pages in this Hacker News website are created:

const path = require('path') 
 
exports.createPages = ({ graphql, boundActionCreators }) => { 
  const { createPage } = boundActionCreators 
  return new Promise((resolve, reject) => { 
    graphql(' 
      { 
        allHnStory(sort: { fields: [order] }, limit: 10) { 
          edges { 
            node { 
              id 
            } 
          } 
        } 
      } 
    ').then(result => { 
      if (result.errors) {
        reject(result.errors) 
      } 
 
      const template = path.resolve('./src/templates/story.js') 
 
      result.data.allHnStory.edges.forEach(({ node }) => { 
        createPage({ 
          path: '/item/${node.id}', 
          component: template, 
          context: { 
            id: node.id, 
          }, 
        }) 
      }) 
 
      resolve() 
    })
}) }

This module exports a createPages() function that Gatsby will use to create the static Hacker News article pages at build time. It starts by using the grapghql() function to execute a query to find all of the article nodes that you need to create pages for:

graphql(' 
  { 
    allHnStory(sort: { fields: [order] }, limit: 10) { 
      edges { 
        node { 
          id 
        } 
      } 
    } 
  } 
') 

Next, the createPage() function is called for each node:

const template = path.resolve('./src/templates/story.js') 
 
result.data.allHnStory.edges.forEach(({ node }) => { 
  createPage({ 
    path: '/item/${node.id}', 
    component: template, 
    context: { 
      id: node.id, 
    },
  }) 
}) 

The properties that are passed to createPage() are:

  • path: This is the URL that when accessed, will render the page.
  • component: This is the filesystem path to the React component that renders the page content.
  • context: This is data that's passed to the React component. In this case, it's important that the component knows the article ID.

This is the general approach that you would take with Gatsby any time you have lots of pages to generate based on dynamic data, but the same React component can be used to render the content. In other words, you would probably rather write this code and a React component rather than separate components for every article.

Let's take a look at the component that's used to render the article details page:

import React from 'react' 
 
import StoryItem from '../components/story-item' 
import Comment from '../components/comment' 
 
const Story = ({ data }) => ( 
  <div> 
    <StoryItem story={data.hnStory} active={true} /> 
    <ul> 
      {data.hnStory.children.map(comment => ( 
        <Comment key={comment.id} data={comment} /> 
      ))} 
    </ul> 
  </div> 
) 
 
export default Story 
 
export const pageQuery = graphql' 
  query StoryQuery($id: String!) { 
    hnStory(id: { eq: $id }) { 
      ...Story 
      children { 
        ...Comment 
      } 
    } 
  } 
' 

Once again, the component relies on Gatsby executing the GraphQL query found in the pageQuery constant. The context is passed to createPage() in gatsby-node.js. This is how you're able to feed the $id argument into the query so that you can query for the specific story data.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.244.216