Gatsby: Fetching Data at The Component Level with useStaticQuery

Queries are no longer relegated to just page level components

Paige Niedringhaus
Bits and Pieces

--

Photo by Lukas Blazek on Unsplash

Introduction

I’m in the process of building myself a new personal website: a site where people can learn more about me, find all the articles I’ve written over time about web development and get in touch if they want to say hi. 👋

Since I’m already quite familiar with the React framework, and this will be a static site lacking the need for a server, protected routes or a separate database, I decided to go with Gatsby to build it.

Gatsby’s been sweeping the web development world since mid 2017, offering a powerful, open source alternative to build “blazing fast” static sites leveraging the React framework and GraphQL for data fetching. It’s opinionated in how you configure a web app, but because of this, building and deploying the site live is remarkably easy. And Gatsby boasts an ever-expanding ecosystem of plugins making so many things possible with a just a few extra lines of configuration code.

All that being said, Gatsby v1 had a drawback to its data fetching which was quite annoying: in v1 you could only fetch data at the page level. What this meant is even if a child component nested several levels deep in a page needed the data, the actual GraphQL query to get that data had to be at the top level component. Then the results had to be passed down through the component tree to get it where it was needed. Not the end of the world, but it also didn’t make for the cleanest, most modular code either.

Gatsby v2 fixed this with the introduction of the StaticQuery API, but the real improvement (in my opinion, at least) came in v2.1 with the introduction of the useStaticQuery Hook, a React Hook to make data fetching in any component super simple. Today, let’s explore how the useStaticQuery Hook at the component level simplifies data fetching throughout a Gatsby site.

Tip: Use Bit (Github) to share, document, and manage reusable React components from different projects. It’s a great way to increase code reuse, speed up development, and build apps that scale.

Example: exploring shared components on Bit.dev

Gatsby v1 Page Level Queries (the old way)

What you may encounter (as I did) if you choose a Gatsby Starter project instead of building your site completely from scratch, is the old style of GraphQL page component queries in these starter repos. Since there’s a real possibility of this happening, I’ll cover what those queries look like and how to convert them into new React Hooks for use at the component level.

gatsby-advanced-starter

Like many people, I chose a very popular Gatsby Starter from their library so I didn’t have to put together my personal site from square one. The starter I chose was the gatsby-advanced-starter because it offered things like SEO, post search by tags or categories, and some starter sample posts already set up, but had no styling to overwrite and was basically a clean slate for me to design as I wished.

While the starter is a great jumping off point, it’s a little outdated now that React Hooks are a mainstream option; the maintainer hasn’t bothered to update it using the latest advances to the React framework. For instance, you’ll see that the starter still uses React’s class-based components for any of the pages requiring state, and all of its data-fetching queries are at the page level too.

Here’s some code examples of what I’m describing. There’s a <PostListing/> component in the starter, which displays the names of the posts and links to each individual post when clicked. In order to get all the post data to display a list of the available posts, the page level component <Landing/> must make the query to fetch the data.

Here’s the code for <Landing/> first.

Landing.jsx

Here’s the parent component of <PostListing/>, which must fetch the post data for the child component to receive as props and render.

As you can see, the <Landing/> component itself needs none of the GraphQL LandingQuery data it’s fetching. The parent component simply destructures the data returned down to the array that the <PostListing/> child component needs, with no use for the data itself.

Now, for the child component that takes in the data and renders the list of posts.

PostListing.jsx

The child component that actually displays the post data in a list for the users.

The <PostList/> component isn’t too complicated, it just takes in the postEdges prop array supplied by the <Landing/> parent, and turns it into an easier-to-iterate over array called postList.

And once postList is constructed, the JSX to render the list iterates over each item in the list via the map function. Done, list of posts rendered to the browser.

Not a terribly difficult example, but tell me it doesn’t seem totally unnecessary in this day and age of stateful functional components to require the parent component to fetch that data to pass to the child when it actually needs none of that data. Lucky for me, the Gatsby team agreed and the useStaticQuery Hook was born.

Now, here’s how to take that query out of the parent and modularize it for the new world of React Hooks.

Gatsby v2 useStaticQuery Hook (the new Hooks way)

In my codebase (which is still a work in progress), which I converted from the components shown above, I cut the LandingQuery out of the <Landing/> component, and created a new Hook file called usePostListingQuery,js, after the style recommended by the React core team for all custom hooks in a project.

Here’s the code for the usePostListingQuery Hook.

usePostListingQuery.js

This is the query removed from the <Landing/> component, and modified slightly using the useStaticQuery hook.

If you compare the GraphQL query code from the original LandingQuery to the one here, you’ll notice there’s not much difference.

The biggest changes are that the useStaticQuery Hook is imported from "gatsby”, and it wraps the original GraphQL query (now renamed to PostQuery to clarify its purpose) and is set equal to a constant variable, PostListData. This whole thing is then wrapped inside an arrow function named usePostListingQuery, and as soon as the data is fetched, it’s returned at the bottom of the function.

And below is how to use this hook (or any other) in any component within a Gatsby application.

PostListing.js

The revised version of the `PostListing` component with the `usePostListingQuery` Hook.

The majority of the PostListing code has not changed from the original.

The biggest difference is that the getPostList() query is now calling the usePostListingQuery Hook instead of relying on props to supply that post data. Whereas the data in the original version was iterated through with this.props.postEdges, it’s now iterated through with posts.allMarkdownRemark.edges instead.

Once that data’s present in the component, it’s looped through in exactly the same way and transformed into the nicer-to-work-with array named postList.

And one of the best bits? This GraphQL query hook can be reused anywhere it might be needed within the code — without having to copy/paste the same query again into the page level component, or pass the data down through several levels of components that don’t need that data (commonly known as “props drilling”, in the React world).

In case you’d like to see, here’s what the updated <Landing/> component looks like as well.

Landing.jsx

Revised <Landing/> component, sans unneeded data fetching for its <PostListing/> child component.

The <PostListing/> component still exists, but the query and the data destructuring are left to the that component itself.

In my eyes, it’s much more straightforward and easy to interpret what’s happening in this component now that the GraphQL query for <PostListing/> has been removed.

Caveats with useStaticQuery

While useStaticQuery is a vast improvement over having to have all GraphQL queries at the top level components in your Gatsby application, there are still a couple of things to keep in mind about deciding if it’s the right solution for your needs.

  • useStaticQuery does not accept variables, but it can be used in any component (pages included)
  • Only a single instance of useStaticQuery can be used in a single JavaScript file (the Gatsby team is already hard at work making this shortcoming a thing of the past, but it is still our current reality, for now).

That’s it though. All in all, I see useStaticQuery as a major improvement to the ease of building a Gatsby site and working with components that require data. And I’m excited to keep using it and see how the team continues to improve the Gatsby developer experience.

Conclusion

Gatsby’s been making waves since it hit the JavaScript scene just a few years ago, boasting the React framework under the hood, coupled with GraphQL data fetching for a wicked fast site experience. And when I finally decided it was time to build a new personal site, I reached for it to help me do so.

The great thing about picking up Gatsby in the time since React Hooks have gone mainstream, is that the Gatsby team hasn’t shied away from adding the ease of hooks into their framework. The useStaticQuery Hook in particular, along with the ability to fetch data in individual components rather than only with page-level components, has really made development with Gatsby easier.

Now, queries can exist as standalone hooks called by any component in the application that needs that data, no query duplication or props passing required. Pretty convenient, if you ask me.

Check back in a few weeks — I’ll be writing more about JavaScript, React, ES6, or something else related to web development.

Thanks for reading. If you’re building a site with Gatsby, hopefully this useStaticQuery hook will simplify your own data fetching within your app. I really appreciate the cleaner, more modularized look and feel of my React components from this hook, and I think you will too.

If you enjoyed reading this, you may also enjoy some of my other free pieces:

--

--

Staff Software Engineer at Blues, previously a digital marketer. Technical writer & speaker. Co-host of Front-end Fire & LogRocket podcasts