Skip to content

Instantly share code, notes, and snippets.

@ismailalabou
Forked from chris-geelhoed/ProductList.js
Created June 23, 2021 15:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ismailalabou/62f219be5e0f208dc4fc1732adf67811 to your computer and use it in GitHub Desktop.
Save ismailalabou/62f219be5e0f208dc4fc1732adf67811 to your computer and use it in GitHub Desktop.
How to Paginate Results with Shopify's GraphQL API
import axios from 'axios'
import React from 'react'
import {
Stack,
Thumbnail,
TextStyle,
Card,
Pagination,
Spinner,
TextContainer
} from '@shopify/polaris'
class ProductList extends React.Component {
constructor(props) {
super(props)
this.state = {
isLoading: false,
itemsPerPage: 5,
page: 0,
results: [],
productsById: {}
}
this.fetchData = this.fetchData.bind(this)
this.storeData = this.storeData.bind(this)
}
get pagedResults() {
const start = this.state.page * this.state.itemsPerPage
const end = start + this.state.itemsPerPage
return this.state.results.slice(start, end)
}
get pagedProductsWithoutData() {
return this.pagedResults.filter(id => !this.state.productsById[id])
}
get pagedProductsWithData() {
return this.pagedResults.filter(id => this.state.productsById[id])
}
async fetchData() {
this.setState({ isLoading: true })
const params = { ids: this.pagedProductsWithoutData }
try {
const res = await axios.get('/api/products/data', {
params
})
this.storeData(res.data)
} catch (e) {
console.error('Could not fetch products', e)
}
this.setState({ isLoading: false })
}
async fetchResults() {
this.setState({ isLoading: true })
try {
const res = await axios.get('/api/products/results')
console.log(res.data)
this.setState({ results: res.data })
} catch (e) {
console.error('Could not fetch results', e)
}
this.setState({ isLoading: false })
}
storeData(products) {
const productsById = Object.assign({}, this.state.productsById)
for (let product of products) {
productsById[product.id] = product
}
this.setState({ productsById })
}
async componentDidMount() {
await this.fetchResults()
await this.fetchData()
}
componentDidUpdate(prevProps, prevState) {
if (prevState.page !== this.state.page && this.pagedProductsWithoutData.length > 0) {
this.fetchData()
}
}
render() {
const numPages = Math.ceil(this.state.results.length / this.state.itemsPerPage)
const cardContents = this.state.isLoading ? (
<Stack
distribution="center"
alignment="center"
>
<div className="spinner-preview-container">
<Spinner
accessibilityLabel="Loading affected products..."
size="large"
color="teal"
/>
</div>
</Stack>
) : (
<Stack vertical>
<Stack vertical>
{this.pagedProductsWithData.map(id => {
const product = this.state.productsById[id]
return (
<Stack
key={product.id}
vertical
spacing="extraTight"
>
<Stack
alignment="center"
distribution="leading"
>
<Thumbnail
source={product.featuredImage && product.featuredImage.transformedSrc}
alt={product.featuredImage && product.featuredImage.altText}
size="small"
/>
<h3>{product.title}</h3>
</Stack>
<TextContainer>
<TextStyle variation="subdued">{product.description}</TextStyle>
</TextContainer>
</Stack>
)
})}
</Stack>
<Stack
alignment="center"
distribution="equalSpacing"
>
<Pagination
hasPrevious={this.state.page > 0}
onPrevious={() => this.setState(state => ({ page: state.page - 1 }))}
hasNext={((this.state.page + 1) * this.state.itemsPerPage) < this.state.results.length}
onNext={() => this.setState(state => ({ page: state.page + 1 }))}
/>
<Stack
vertical
alignment="trailing"
spacing="extraTight"
>
<TextStyle>
Page {this.state.page + 1}/{numPages}
</TextStyle>
<TextStyle
variation="subdued"
>
(Max {this.state.itemsPerPage} products per page)
</TextStyle>
</Stack>
</Stack>
</Stack>
)
return (
<Card
title="Products"
sectioned
>
<Stack vertical>
{this.state.results.length > 0 && <TextStyle>{this.state.results.length} results:</TextStyle>}
{cardContents}
</Stack>
</Card>
)
}
}
export default ProductList
<?php
namespace App\Handlers;
use Osiset\BasicShopifyAPI;
class QueryHandler
{
public static function fetch(BasicShopifyAPI $api, array $product_ids): array
{
$product_id_json = json_encode($product_ids);
$q = <<<QUERY
{
nodes(ids: $product_id_json) {
...on Product {
title,
id,
description,
featuredImage {
altText,
transformedSrc
}
}
}
}
QUERY;
$request = $api->graph($q);
return $request->body->nodes;
}
public static function results(BasicShopifyAPI $api): array
{
$ids = [];
$cursor = null;
do {
$args = [
"first: 250"
];
if (!empty($cursor)) {
$args[] = "after: \"$cursor\"";
}
$args = implode(', ', $args);
$q = <<<QUERY
{
products($args) {
pageInfo {
hasNextPage,
hasPreviousPage
},
edges {
cursor,
node {
id,
title
}
}
}
}
QUERY;
$request = $api->graph($q);
if (!empty($request->body->products->edges)) {
$edges = $request->body->products->edges;
foreach ($edges as $edge) {
$ids[] = $edge->node->id;
$cursor = $edge->cursor;
}
}
} while ($request->body->products->pageInfo->hasNextPage);
return $ids;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment