vercel / commerce

Next.js Commerce
https://demo.vercel.store
MIT License
11.5k stars 4.25k forks source link

Shopify Pagination #1202

Open DundarKoray opened 1 year ago

DundarKoray commented 1 year ago

Currently, there is no pagination implementation. What happens if there are so many products? Shopify has a limitation of 250 products to fetch at once, if there are let's say 1000 products. Somehow getProducts query needs to be updated something similar to the below,

query pagination($numProducts: Int!, $cursor: String){
  products(first: $numProducts, after: $cursor) {
    nodes {
      title
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

Let's say we want to show 20 products and show more button. { "numProducts": 20, "cursor": "eyJsYXN0X2lkIjo4NDg3MDAwNzAzMjkxLCJsYXN0X3ZhbHVlIjoiODQ4NzAwMDcwMzI5MSJ9" }

thomasbritton commented 1 year ago

I ran into the same issue basically, especially in the scenario where you are showing say 50 or more products on a 'page' and someone paginates to page 6 and the user presses the back button, you can't load the all of the previous products because of the limit.

Would love to know of a working solution and I couldn't find any headless solutions after many hours of searching that offered any type of pagination solution other than to just keep showing the next pages and losing the state so you would always go back to the first page of products

zpuckeridge commented 11 months ago

Likewise running into a similar problem. Doesn't appear to be a clear way to implement pagination. Was able to get the cursor to load the next page of items, but this introduced a multitude of issues elsewhere.

Nikojuu commented 8 months ago

Im stuck with trying to implement Pagination with this template any one got simple aproach? it feels like im hitting a wall no matter what aproach i try use,

I know it should be cursor based pagination if i store cursor in url then i would loose previous results as page refresh and i cant use hooks on server components.

My last resort was to try make client component with load more button and fetch more data but that also feels pretty hard as everything in this template is deeply nested making it difficult to change anything without having some problems elsewhere.

Also this is same for both Search page with all collections and seperate [collection] page.tsx current code just fetch everything at once isnt that kinda bad in real life alot ecommerces have 1000+ products?

megetron commented 5 months ago

any progress on this one?

megetron3 commented 2 months ago

for me without pagination the ecommerce backend crashed.

i had to add the server side pagination with support for page and pageSize.

also added a new component:

import Link from 'next/link';
import clsx from 'clsx';

const Pagination = ({
  currentPage,
  totalCount,
  url
}: {
  currentPage: Number;
  totalCount: Number;
  url: string;
}) => {
  const totalPages = totalCount; // assuming the total number of pages is provided directly

  const getPageNumbers = () => {
    return [...Array(totalPages).keys()].map((i) => i + 1);
  };

  const pageNumbers = getPageNumbers();

  return (
    <nav aria-label="Navigation">
      <ul className="list-style-none flex">
        <li>
          <Link
            className={clsx(
              Number(currentPage) <= 1
                ? 'bg-primary-100 text-primary-700 dark:text-primary-500 pointer-events-none relative block rounded px-3 py-1.5 text-sm font-medium transition duration-300 focus:outline-none dark:bg-slate-900'
                : 'text-surface focus:text-primary-700 active:text-primary-700 dark:focus:text-primary-500 dark:active:text-primary-500 relative block rounded bg-transparent px-3 py-1.5 text-sm transition duration-300 hover:bg-neutral-100 focus:bg-neutral-100 focus:outline-none active:bg-neutral-100 dark:text-white dark:hover:bg-neutral-700 dark:focus:bg-neutral-700 dark:active:bg-neutral-700'
            )}
            href={`${url}?page=${Math.max(Number(currentPage) - 1, 1)}`}
            aria-label="Previous"
            aria-diabled={Number(currentPage) === Number(totalPages)}
            tabIndex={Number(currentPage) === Number(totalPages) ? -1 : undefined}
          >
            <span aria-hidden="true">&laquo;</span>
          </Link>
        </li>

        {pageNumbers.map((number) => (
          <Link
            key={number}
            href={`${url}?page=${number}`}
            className={`text-surface focus:text-primary-700 active:text-primary-700 dark:focus:text-primary-500 dark:active:text-primary-500 relative block rounded bg-transparent px-3 py-1.5 text-sm transition duration-300 hover:bg-neutral-100 focus:bg-neutral-100 focus:outline-none active:bg-neutral-100 dark:text-white dark:hover:bg-neutral-700 dark:focus:bg-neutral-700 dark:active:bg-neutral-700 ${currentPage === number ? 'active' : ''}`}
          >
            {number}
          </Link>
        ))}
        <li>
          <Link
            className={clsx(
              Number(currentPage) >= Number(totalPages)
                ? 'bg-primary-100 text-primary-700 dark:text-primary-500 pointer-events-none relative block rounded px-3 py-1.5 text-sm font-medium transition duration-300 focus:outline-none dark:bg-slate-900'
                : 'text-surface focus:text-primary-700 active:text-primary-700 dark:focus:text-primary-500 dark:active:text-primary-500 relative block rounded bg-transparent px-3 py-1.5 text-sm transition duration-300 hover:bg-neutral-100 focus:bg-neutral-100 focus:outline-none active:bg-neutral-100 dark:text-white dark:hover:bg-neutral-700 dark:focus:bg-neutral-700 dark:active:bg-neutral-700'
            )}
            href={`${url}?page=${Math.min(Number(currentPage) + 1, Number(totalPages))}`}
            aria-label="Next"
            aria-diabled={Number(currentPage) === Number(totalPages)}
            tabIndex={Number(currentPage) === Number(totalPages) ? -1 : undefined}
          >
            <span aria-hidden="true">&raquo;</span>
          </Link>
        </li>
      </ul>
    </nav>
  );
};

export default Pagination;

on the search page added:

import Pagination from 'components/pagination';
...
  const { sort, page = 1 } = searchParams as { [key: string]: string };
  const currentPage = parseInt(page as string, 10);
   products = await getProducts({ query, sortKey, page: currentPage });
...

  return (
    <section>
      {products.length === 0 ? (
        <p className="py-3 text-lg">{`No products found in this collection`}</p>
      ) : (
        <>
          <Grid className="grid-cols-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
            <ProductGridItems products={products} />
          </Grid>
          <Pagination
            currentPage={currentPage}
            totalCount={products.length}
            url={`/search/${params.collection}`}
          />

          <></>
        </>
      )}
    </section>

i would create pr for that but don't work on a shopify ecommerce, and my current project very different from the original nextjs commerce.