Non
Non

Reputation: 8589

I am unable to create pages dynamically using Gatsby

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: enter image description here

Any ideas on where could I be failing at?

Upvotes: 2

Views: 1618

Answers (2)

xadm
xadm

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:

  • query for all products (all data) at once;
  • query only for data segments (5-items recordsets/chunks) required for current iteration.

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

Ferran Buireu
Ferran Buireu

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

Related Questions