A space to discuss GraphQL queries, mutations, troubleshooting, throttling, and best practices.
I'm trying to use apollo fetchMore to fetch paginated results from the graphQL endpoint. Here is what I've got so far. It does the first query successfully, but it does not try to load additional pages. Any ideas on what I'm doing wrong?
import gql from "graphql-tag"; import { Query } from "react-apollo"; import { Page } from "@shopify/polaris"; const GET_PRODUCTS = gql` query getProducts($cursor: String) { products(first: 1, after: $cursor) { pageInfo { hasNextPage } edges { node { id title descriptionHtml } cursor } } } `; class Index extends React.Component { state = { products: [] }; render() { return ( <Page> <Query query={GET_PRODUCTS}> {({ data, loading, error, fetchMore }) => { <getProducts onLoadMore={() => { console.log("LOAD MORE"); fetchMore({ variables: { cursor: data.products.edges[0].cursor }, updateQuery: (previousResult, { fetchMoreResult }) => { console.log("FETCH MORE RESULTS", fetchMoreResult); if (fetchMoreResult.products.edges.length) { return [ ...previousResult.products.edges, ...fetchMoreResults.products.edges ]; } return previousResult.products.edges; } }); }} />; if (loading) return <div>loading products to memory...</div>; if (error) return <div>{error.message}</div>; return <div>success!</div>; }} </Query> </Page> ); } } export default Index;
Alright, I re-worked the Query component and seem to have it working. Is there a better way to do this?
<Query query={GET_PRODUCTS}> {({ data, loading, error, fetchMore }) => { if (loading) return <div>loading products to memory...</div>; if (error) return <div>{error.message}</div>; if (data.products.pageInfo.hasNextPage) { fetchMore({ variables: { cursor: data.products.edges[data.products.edges.length - 1].cursor }, updateQuery: (previousResult, { fetchMoreResult }) => { let combinedData = { products: { pageInfo: { ...fetchMoreResult.products.pageInfo }, edges: [ ...previousResult.products.edges, ...fetchMoreResult.products.edges ], __typename: fetchMoreResult.products.__typename } }; return combinedData; } }); } console.log("HERE", data); return <div>{JSON.stringify(data)}</div>; }} </Query>
@find-replace I'm having trouble at this same point and haven't been able to get fetchMore working. Did you ever work out a better solution?
I ended up with this which works fine.
<Query query={GET_PRODUCTS}>
{({ data, loading, error, fetchMore, refetch }) => {
if (loading) return <Loading />;
if (error) {
console.log("ERROR", error);
return <div>{error.message}</div>;
}
if (data.products.pageInfo.hasNextPage) {
fetchMore({
variables: {
cursor:
data.products.edges[data.products.edges.length - 1]
.cursor
},
updateQuery: (previousResult, { fetchMoreResult }) => {
console.log("FETCH MORE", data.products.pageInfo);
return {
products: {
pageInfo: {
...fetchMoreResult.products.pageInfo
},
edges: [
...previousResult.products.edges,
...fetchMoreResult.products.edges
],
__typename: fetchMoreResult.products.__typename
}
};
}
});
}
if (!data.products.pageInfo.hasNextPage) {
console.log("DONE", data.products.pageInfo);
return (
<Find products={data.products.edges} refetch={refetch}>
Done
</Find>
);
}
return <Loading />;
}}
</Query>
It looks like this solution is pretty much exactly how the Apollo docs recommend when using fetchMore and updateQuery. Although I couldn't work it out until I saw your comment and also this video. The video is good to follow as the API schema he's working with is similar to Shopify's.
Your solution to getting the cursor is good too!
It's disappointing that Shopify don't go into detail about how to do this. They have a video talking about cursor pagination, but they manually enter the cursor which is far from useful.
Here's my working code for what it's worth..
import React from "react";
import { gql, useQuery } from "@apollo/client";
const PRODUCTS_GQL = gql`
query ProductsGql($cursor: String) {
products(first: 5, after: $cursor) {
edges {
cursor
node {
id
title
handle
}
}
pageInfo {
hasNextPage
}
}
}
`;
const ListProducts = () => {
const { data, error, loading, fetchMore } = useQuery(PRODUCTS_GQL);
if (error) return <p>Error: {error.message}</p>;
if (loading || !data) return <p>Loading..</p>;
return (
<>
<button
onClick={() => {
fetchMore({
variables: {
cursor: data.products.edges[data.products.edges.length - 1].cursor
},
updateQuery: (previousResult, { fetchMoreResult }) => {
let combinedData = {
products: {
pageInfo: { ...fetchMoreResult.products.pageInfo},
edges: [
...previousResult.products.edges,
...fetchMoreResult.products.edges
],
__typename: fetchMoreResult.products.__typename
}
}
return combinedData;
}
})
}}
>
Load more
</button>
<div>
{data.products.edges.map(( product ) => (
<p key={product.node.id}>{product.node.title}</p>
))}
</div>
</>
)
}
export default ListProducts;