Reputation: 16481
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;
Upvotes: 0
Views: 691
Reputation: 80041
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,
}
})
})
}
Upvotes: 1