Separating data queries from rendering components

Up until now, most of the example Gatsby pages that we’ve seen have co-located data queries and data rendering in the same file to make it easier to connect the dots between a data query and the renderer that handles the response data. Many developers prefer to formalize a separation of concerns that distinguishes between components of code that are responsible for querying data and others that perform rendering.

In Gatsby themes, separating data queries into independent components distinct from those handling rendering is often preferable to the co-location common in Gatsby pages. This is due to the fact that Gatsby themes often provide code components that are intended to be shadowed (i.e., overridden).

Without a clean separation between data query code and rendering code, a theme user would need to understand both how to issue page queries and how to perform JSX rendering. When those components are separated into distinct files, it becomes much easier for theme consumers to override individual JSX components (e.g., BlogPostList or UserCard) without having to rewrite the page query or StaticQuery providing the data.

If you need to separate out page queries so they can be overridden, you can use a typical Gatsby template file to perform the necessary data collection and then provide that data to a separate component—here, a JSX component known as <BlogPostList /> (src/components/blog-post-list.js):

// example/src/templates/blog-post-list.js
import React from "react"
import { graphql } from "gatsby"
import PostList from "../components/blog-post-list"

export default function MyPostsList(props) {
  return <BlogPostList posts={props.allMdx.edges} />
}

export const query = graphql`
  query {
    allMdx(
      sort: { 
        order: DESC,
        fields: [frontmatter___date]
      }
      filter: {
        frontmatter: {
          draft: {
            ne: true
          }
        }
      }
    ) {
      edges {
        node {
          id
          frontmatter {
            title
            path
            date(formatString: "MMMM DD, YYYY")
          }
        }
      }
    }
  }
`

If you need to separate out static queries from a non-page component so they can be overridden, you can use the layout component to pass the response data from the data query to smaller rendering components as React props. In the following example, our layout component issues a static query to fetch site metadata, which is then used to populate smaller components within the layout:

// example/src/components/layout.js
import React from "react"
import { useStaticQuery, graphql } from "gatsby"

import Header from "../header.js"

const Layout = ({ children }) => {
  const {
    site: { siteMetadata },
  } = useStaticQuery(
    graphql`
      query {
        site {
          siteMetadata {
            title
          }
        }
      }
    `
  )

  const { title } = siteMetadata

  return (
    <>
      <Header title={title} />
      <main>{children}</main>
    </>
  )
}

export default Layout

Whether you perform data querying from the standpoint of a page component (with typical page queries) or a non-page component (with static queries), it’s important to allow theme users who wish to override functionality to shadow either the data querying or the rendering mechanism rather than struggling with a component that does both. This is one way that themes differ from normal page queries: they are intended to be readable by other developers, not just ourselves or our immediate developer teams.


Posted

in

,

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *