0

I've created a React static query hook that requests site and page level meta data. At the moment I'm duplicating this code on every page build as 1 part of the query changes from page to page allProductsJson, allProductJson etc etc. I was wondering if it is possible to remove this duplication by using the querying capabilities more efficiently? I've read that Gatsby static queries don't support arguments, but maybe there's a way to query all meta and filter by page?

Example of a duplicated query hook:

import { graphql, useStaticQuery } from 'gatsby';
import pageMetadataSelector from 'utils/page-metadata-selector';
import siteMetadataSelector from 'utils/site-metadata-selector';
import getAbsoluteUrl from 'utils/get-absolute-url-path';

const QUERY = graphql`
  {
    pageMetadata: allProductsJson {
      nodes {
        metadata {
          title
          description
          image {
            publicURL
          }
          robots
        }
      }
    }
    site {
      siteMetadata {
        siteUrl
      }
    }
  }
`;

const usePageMetadataQuery = ({ location }) => {
  const { pageMetadata, site } = useStaticQuery(QUERY);
  const pageData = pageMetadataSelector(pageMetadata);
  const { siteUrl } = siteMetadataSelector(site);

  return {
    metadata: {
      ...pageData,
      siteUrl: getAbsoluteUrl(siteUrl, location.pathname),
      image: getAbsoluteUrl(siteUrl, pageData.image.publicURL)
    }
  };
};

export default usePageMetadataQuery;
5
  • prepare external gql files, import (gql loader), choose/select by parameter Commented Sep 7, 2020 at 12:22
  • Could you explain this with an example @xadm Commented Sep 7, 2020 at 13:34
  • github.com/apollographql/graphql-tag#importing-graphql-files ... gatsby specific gatsbyjs.com/plugins/gatsby-plugin-graphql-loader .. but you need it at build time Commented Sep 7, 2020 at 13:41
  • @xadm I want to do the query at build time, it doesn't look like this plugin supports that: > This is not for: Loading queries used at build time. Commented Sep 7, 2020 at 15:41
  • yes, I wrote that ... 1st link rather for info or manual config... and more proper plugin for use gatsbyjs.com/plugins/gatsby-plugin-graphql-tag-loader Commented Sep 7, 2020 at 16:33

1 Answer 1

1

Given what you’re doing here, I think you would be better served by adding the relevant data to the page context in gatsby-node.js (in the createPage call), then using wrapRootElement (in gatsby-browser.js and gatsby-ssr.js) to extract those details from the page context.

From there you can either render the component you need directly or pass it to a React Context Provider for consumption by any children.

Here’s an example showing the latter:

export const wrapPageElement = ({ element, props: { context: { metadata} } }) => {
  return (
    <MetadataContext.Provider value={metadata}>
      {element}
    </MetadataContext.Provider>
  )
}

It’s also possible to fetch this data in each of your page queries and access it in the same way (destructure data instead of context):

exports.createPages = async ({ graphql, actions }) => {
  const {
    data: {
      pageMetadata,
      pageData,
    }
  } = await graphql(`
    {
      pageMetadata: allProductsJson {
        nodes {
          slug
          metadata {
            title
            description
            image {
              publicURL
            }
            robots
          }
        }
      }

      pageData: allPageJson {
        pages: {
          slug
        }
      }
    }
  `

  // for each page
  pageData.pages.map(({ slug }) => {
    // select the metadata with the matching slug
    const { metadata } = pageMetadata.nodes.find(node => node.slug === slug)
    
    // create the page
    actions.createPage({
      path: `/${slug}/`,
      component: `./src/templates/page.jsx`,
      context: { 
        // pass both the slug and metadata as context
        slug,
        metadata,
      }
    })
  })
}
Sign up to request clarification or add additional context in comments.

8 Comments

question is about using slightly different queries always aliased to pageMetadata and processed the same way
@coreyward the metadata will change from page to page so I could be querying allProductsJson and then allProductJson etc, wondering how I could get this flexibility?
Right. Rather than plucking the data you need from allProductJson in the page query or a static query you just do it in gatsby-node.js and pass the relevant slice of it as context to the createPage call.
Otherwise you can use page queries, which are run for each page, to fetch this data. Also, you can still grab the location from the props prop passed to wrapPageElement, destructuring it just like I do context in the example above.
@coreyward apologies for coming back to this really late, the part I'm still not sure about is if I add the query to gatsby-node how can I pluck allProductJson then possibly allStoresJson and return that slice as context?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.