Reputation: 8589
I am using Gatsby on my project. I am trying to create a page which needs pagination.
I followed this https://www.gatsbyjs.org/docs/adding-pagination/ and this other guide https://nickymeuleman.netlify.app/blog/gatsby-pagination/ and how to do it but it is not working.
I have multiple queries on gatsby-node.js
:
exports.createPages = async ({ graphql, actions, page }) => {
const { createPage } = actions
const shop = path.resolve("./src/templates/shop.js")
const productPageTemplate = path.resolve("./src/templates/ProductPage/index.js")
const singleProduct = await graphql(`
query {
allShopifyProduct {
edges {
node {
handle
}
}
}
}
`)
// This is the query which I need for the pagination
// Create shop pages
const products = await graphql(`
query allShopifyProduct($skip: Int!, $limit: Int!) {
allShopifyProduct(sort: { fields: [createdAt], order: DESC }
skip: $skip
limit: 5
) {
edges {
node {
id
title
handle
}
}
}
}
`)
const posts = products.data.allShopifyProduct.edges
const postsPerPage = 5
const numPages = Math.ceil(posts.length / postsPerPage)
Array.from({ length: numPages }).forEach((_, i) => {
const withPrefix = (pageNumber) =>
pageNumber === 1 ? `/shop` : `/shop/${pageNumber}`
const pageNumber = i + 1
createPage({
path: withPrefix(pageNumber),
component: shop,
context: {
limit: postsPerPage,
skip: i * postsPerPage,
current: pageNumber,
total: numPages,
hasNext: pageNumber < numPages,
nextPath: withPrefix(pageNumber + 1),
hasPrev: i > 0,
prevPath: withPrefix(pageNumber - 1),
},
})
})
// Adding the single product stuff to show my multiple queries
singleProduct.data.allShopifyProduct.edges.forEach(({ node }) => {
createPage({
path: `/product/${node.handle}/`,
component: productPageTemplate,
context: {
// Data passed to context is available
// in page queries as GraphQL variables.
handle: node.handle,
},
})
})
})
This is my React component:
import React from "react"
import { graphql, Link } from "gatsby"
// Components
import ProductGrid from "@components/ProductGrid/productGrid"
const Shop = ({ data: { allShopifyProduct }, pageContext }) => {
return (
<div className="product-grid">
<ProductGrid allShopifyProduct={allShopifyProduct}
/>
{!pageContext.hasPrev && (
<Link to={pageContext.prevPage} rel="prev">
← Previous Page
</Link>
)}
{!pageContext.hasNext && (
<Link to={pageContext.nextPage} rel="next">
Next Page →
</Link>
)}
</div>
)
}
export default Shop
export const query = graphql`
query allShopifyProduct($skip: Int!, $limit: Int!) {
allShopifyProduct(
sort: { fields: [createdAt], order: DESC }
skip: $skip
limit: $limit
) {
edges {
node {
id
title
handle
}
}
}
}
`
And it keeps throwing errors like these:
ERROR #85927 GRAPHQL
There was an error in your GraphQL query:
Variable "$skip" is never used in operation "allShopifyProduct".
See if $skip has a typo or allShopifyProduct doesn't actually require this variable.
File: gatsby-node.js:74:26
ERROR #85927 GRAPHQL
There was an error in your GraphQL query:
Variable "$limit" is never used in operation "allShopifyProduct".
See if $limit has a typo or allShopifyProduct doesn't actually require this variable.
File: gatsby-node.js:74:26
ERROR #11321 PLUGIN
"gatsby-node.js" threw an error while running the createPages lifecycle:
Cannot read property 'allShopifyProduct' of undefined
108 | `)
109 |
> 110 | const posts = products.data.allShopifyProduct.edges
| ^
111 |
File: gatsby-node.js:110:31
TypeError: Cannot read property 'allShopifyProduct' of undefined
- gatsby-node.js:110 Object.exports.createPages
/Users/marcelo/Work/gatsby-on-demand/gatsby-node.js:110:31
failed createPages - 0.113s
If I run these exact queries on my GraphiQL interface everything work perfectly:
Any ideas on where could I be failing at?
Upvotes: 2
Views: 1618
Reputation: 8418
At the beginning ... method for passing parameters/values used in queries (as variables) ... you should pass them (values) to variables in your "looping" query:
let myLoopingSkip = 0; // starting skip
// Create shop pages
const products = await graphql(`
query allShopifyProduct($skip: Int!, $limit: Int!) {
allShopifyProduct(sort: { fields: [createdAt], order: DESC }
skip: $skip
limit: $limit
) {
edges {
node {
id
title
handle
}
}
}
}
`, { skip: myLoopingSkip, limit: 5 } ); // variables used in query
Then you can build a "outer" loop over entire (query+createPages for paginated list and single products) block passing current myLoopingSkip
value to query variable skip
.
Two looping scenarios possible:
1st option is easy but can be resource hungry on big datasets, it can just crash at some moment.
2nd (IMHO better) option is much more reliable but needs additional/separate query for numPages
(amount of all products) before looping. It is also required for conditional next page
.
In real life it's unusual to browse after 10 page ... in practice even google stops showing results after some page limit ... but product page must be linked somwhere to be accessible/crawled - IMHO it's better to have shorter paginated listings by category.
If you don't really need numPages
you can simply keep looping by adding 5 (your 'limit'/postsPerPage
) as long as queried data (result) contains 5 records. In this case "outer loop" can look like:
const postsPerPage = 5;
let myLoopingSkip = 0; // starting skip
do {
// read only data you need in current iteration
let products = await graphql(PAGINATED_PRODUCTS_QUERY,
{ skip: myLoopingSkip, limit: postsPerPage } );
// paginated list
createPage({
path: withPrefix(pageNumber),
component: shop,
context: {
limit: postsPerPage,
skip: i * postsPerPage,
...
// but you can just pass fetched data
// as is done for product page
// no need for query inside component
// just loop over `data` prop to create grid view
//
// createPage({
// path: withPrefix(pageNumber),
// component: shop,
// context: {
// data: products.data.allShopifyProduct.edges,
// loop for single products pages
products.data.allShopifyProduct.edges.map( (node) => {
createPage({
path: `/product/${node.handle}/`,
component: productPageTemplate,
context: {
// Data passed to context is available
// in page queries as GraphQL variables.
handle: node.handle,
// ... but we already have all data here
// ... again, no query needed
// data: node
}
});
myLoopingSkip += postsPerPage;
} while( products.data.allShopifyProduct.edges.length===postsPerPage )
// or use total page condition
Upvotes: 1
Reputation: 29320
You must provide those variables in your context inside createPage
API:
createPage({
path: `/product/${node.handle}/`,
component: productPageTemplate,
context: {
skip: 0 // or any variable
limit: 5 // or any variable
handle: node.handle, // is it used? If don't, you can remove it
},
})
Since you are using as a non-nullable skip
and limit
variables in your query (marked with the exclamation mark, !
) here:
query allShopifyProduct($skip: Int!, $limit: Int!)
They need to exist so you need to provide them (via context) in the same way you showed up in the GraphQL query playground.
Your handle
variable seems to be unused, at least in the code provided, in that case, you can remove it.
More information about GraphQL Schema and Types.
Upvotes: 2