Pagination
There is a small amount of plumbing that you will need to do in the shop in
order to get pagination working. The most complex parts have been abstracted
away in to a Core component called PaginationProvider
, which in most cases
you will not need to use directly. This component handles getting and setting
the correct URL parameters for pagination (?page=5
etc).
Setting up pagination
The example below is for a category page, but can be adapted for any page that uses pagination.
You will need to use the PaginationContext
from Core, and the
PaginationBehaviour
from UI.
import PaginationContext from "@jetshop/core/components/Pagination/PaginationContext";
import { PaginationBehaviour } from "@jetshop/ui/Pagination";
PaginationContext
provides some render prop arguments that
PaginationBehaviour
needs in order to set up the UI elements for
controlling pagination. You'll need to use the Consumer
from
PaginationContext
to pass through the required values:
<PaginationContext.Consumer>
{({ currentPage, goToPage, perPage, setProductsPerPage }) => (
<PaginationBehaviour
currentPage={currentPage}
goToPage={goToPage}
perPage={perPage}
total={category.products.totalResults}
>
{(paginationProps) => <Pagination {...paginationProps} />}
</PaginationBehaviour>
)}
</PaginationContext.Consumer>
As you can see, we take the currentPage
, goToPage
fn, and perPage
, and
pass them straight in to the PaginationBehaviour
component, along with the
category's totalResults field (from the category route query). The full
interface of PaginationContext is as follows:
interface PaginationState {
/** The current page number */
currentPage: number;
/**
* Current offset (e.g. if we are on the second page and there are 10 items
* per page, this will be 10)
*/
offset: number;
/** Number of items displayed per page */
perPage: number;
/** Navigate to given page number */
goToPage: (page: number) => void;
/** Set amount of products per page */
setProductsPerPage: (perPage: number) => void;
}
Note that you don't have to directly wrap PaginationBehaviour
like we have
above. If you like, you could wrap your entire Category page with
PaginationContext.Consumer
, and then use the currentPage
somewhere for
display purposes.
PaginationBehaviour
import { PaginationBehaviour } from "@jetshop/ui/Pagination";
This component renders its children with some arguments that help to construct the actual pagination UI. The full interface is as follows:
interface IPaginationBehaviourRenderProps {
/** The total number of items */
totalItems: number;
/** The total number of pages */
totalPages: number;
/** The current page number */
currentPage: number;
/** Navigate to the next page */
nextPage: () => void;
/** Navigate to the previous page */
prevPage: () => void;
/** Navigate to given page number */
goToPage: (page: number) => void;
// Update number of products displayed per page
setProductsPerPage: (perPage: number) => void;
/** Whether the current page is the first */
isFirstPage: boolean;
/** Whether the current page is the last */
isLastPage: boolean;
/** Whether there are pages to render */
hasPages: boolean;
}
Pagination (UI)
In the example at the top of this document, we've passed all of these args
directly in to the Pagination
component. This is a basic pagination
implementation in UI:
import { Pagination } from "@jetshop/ui/Pagination";
This will render a Previous button, a Next button, and a Page {currentPage} of {totalPages}
readout. Of course you are free to use your own component
for pagination by piecing together the arguments provided by
PaginationBehaviour
. The basic implementation in UI is shown below:
return hasPages ? (
<>
<button onClick={prevPage} disabled={isFirstPage}>
{formatMessage("Prev")}
</button>
<div>
{formatMessage(`Page {currentPage} of {totalPages}`, {
currentPage,
totalPages,
})}
</div>
<button onClick={nextPage} disabled={isLastPage}>
{formatMessage("Next")}
</button>
</>
) : null;
Setting products per page
By default the pagination is set to display 16 products per page. In order to
change the default value, supply the defaultProductsPerPage
prop to the
PaginationProvider
in your Shop.js
file. If you don't have a
PaginationProvider
in there, you're based on an older version of the framework and
you can just add it.
import PaginationProvider from "@jetshop/core/components/Pagination/PaginationProvider";
<PaginationProvider defaultProductsPerPage={12}>
<Switch>
<Route exact path="/" component={LoadableStartPage} />
<Route path="/search" component={LoadableSearchPage} />
<Route
path="/preview"
render={(props) => (
<PreviewRoute
productPage={LoadableProductPage}
productQuery={ProductPreviewQuery}
categoryQuery={CategoryPreviewQuery}
categoryPage={LoadableCategoryPage}
StartPage={LoadableStartPage}
{...props}
/>
)}
/>
<DynamicRoute
routeQuery={routeQuery}
productPage={LoadableProductPage}
categoryPage={LoadableCategoryPage}
contentPage={LoadableContentPage}
notFoundPage={NotFound}
LoadingPage={LoadingPage}
/>
</Switch>
</PaginationProvider>;
If you want to set the defaultProductsPerPage for a specific part of the app,
e.g. the search page, just wrap that component in its own PaginationProvider
and set the defaultProductsPerPage
.
If you'd like to allow the end user to change the amount of items shown per
page, you can use the setProductsPerPage
method from PaginationContext
.
// Update number of products displayed per page
setProductsPerPage: (perPage: number) => void;
Example:
<PaginationContext.Consumer>
{({ currentPage, goToPage, perPage, setProductsPerPage }) => (
<PaginationBehaviour
currentPage={currentPage}
goToPage={goToPage}
perPage={perPage}
total={category.products.totalResults}
>
{(paginationProps) => (
<Pagination {...paginationProps} setPerPage={setProductsPerPage} />
)}
</PaginationBehaviour>
)}
</PaginationContext.Consumer>
Or, a simple example using React hooks:
import { usePagination } from "@jetshop/core/components/Pagination/PaginationProvider";
function UpdatePerPage() {
const { setProductsPerPage } = usePagination();
return (
<button
onClick={() => {
setProductsPerPage(48);
}}
>
Set products per page to 48
</button>
);
}
Depending on your design, you may want to use a select dropdown to allow the user to set the perPage number.
This perPage
value will be persisted throughout the store
via a cookie.
Note that the API is limited to 100 products per page, and if you're approaching that many products per page, consider having a look at windowing (with e.g. react-window).
Changing the query param used for pagination
By default, pagination uses the query parameter 'page'. If you'd like to change that, you can pass a prop queryParam
to PaginationProvider
:
<PaginationProvider queryParam="pagenum">