A space to discuss GraphQL queries, mutations, troubleshooting, throttling, and best practices.
I want to implement cursor based pagination have not managed to get the "hasNext" click to return the next page of results. The area I am having trouble with is the fetchMore and updateQuery section. I've followed:
My GraphQL query works when manually passing the intended variables:
const GET_NEXT_PRODUCTS = gql`
query ($cursor: String){
products(first: 10, after: $cursor) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
cursor
node {
id
title
handle
description
images(first: 1) {
edges {
node {
originalSrc
altText
}
}
}
variants(first: 1) {
edges {
node {
taxCode
price
id
taxable
}
}
}
}
}
}
}
`
My Query component is here:
<Query query={GET_NEXT_PRODUCTS}
notifyOnNetworkStatusChange
>
{({ data, loading, error, fetchMore }) => {
if (loading) return <div>
<Frame>
<Spinner accessibilityLabel="Spinner" size="large" color="teal" />
</Frame>
</div>;
if (error) return <div>
<Banner
title={error.message}
status="critical"
>
<p>{error.stack}</p>
</Banner>
</div>;
console.log(data);
return (
<Page
fullWidth
title="Products"
>
<Card>
<ResourceList
showHeader
items={data.products.edges}
promotedBulkActions={promotedBulkActions}
bulkActions={bulkActions}
resourceName={resourceName}
renderItem={renderItem}
selectedItems={selectedItems}
onSelectionChange={setSelectedItems}
/>
</Card>
{data.products &&
data.products.pageInfo.hasNextPage && (
<Pagination
hasPrevious
onPrevious={() => {
console.log('Previous');
}}
hasNext
onNext={() => {
console.log('Clicked Next');
console.log(data.products.edges);
console.log('0: ' + data.products.edges[0].cursor);
console.log('9: ' + data.products.edges[numProducts - 1].cursor);
fetchMore({
query: GET_NEXT_PRODUCTS,
variables: {
cursor: cursor
},
updateQuery: (previousResult, { fetchMoreResult }) => {
const previousEntry = previousResult.products;
const newProducts = fetchMoreResult.products;
const newCursor = fetchMoreResult.products.edges[numProducts - 1].cursor;
return {
cursor: newCursor,
entry: {
products: newProducts
}
};
}
})
}}
>
</Pagination>
)
}
</Page>
);
}}
</Query>
Right now I have just tried implementing the hasNext but also need to understand how to get the hasPrevious and return the earlier page.
I have not found easy to understand documentation on this specific step and would really appreciate any guidance on how to implement this.
Solved! Go to the solution
This is an accepted solution.
I render the cursor value from before and after into the controls for prev and next buttons.
When I get data, if there is a next or previous page I am simply echoing that out as booleans into my components. So if next is true, a click on the next sends the after cursor to the query. I get back the next page. Same for previous. If no previous page exists, the button, she no work. Simple.
The one thing I learned from all this, that I did not know, was it is OK to provide a before or after cursor with a nil String value! With that, I can limit my GQL to only two queries. Before and After. I used to think (naively) that I needed an initializer, to setup the case where before or after are nil. Silly me.
I never use JS and Apollo so I cannot help with that. I just use Axios to send a request for data to my App, and my App returns data. I think Apollo is for fancy smancy Apps way beyond anything I would use with Shopify. It seems nice though. I guess once you get used to it as a thing, it makes sense to use it. I just hate adding an extra 100,000 lines of someone else's JS to a project, and trying to keep up with those Joneses. It is hard enough just keeping up with the basic CRUD.
This is an accepted solution.
I render the cursor value from before and after into the controls for prev and next buttons.
When I get data, if there is a next or previous page I am simply echoing that out as booleans into my components. So if next is true, a click on the next sends the after cursor to the query. I get back the next page. Same for previous. If no previous page exists, the button, she no work. Simple.
The one thing I learned from all this, that I did not know, was it is OK to provide a before or after cursor with a nil String value! With that, I can limit my GQL to only two queries. Before and After. I used to think (naively) that I needed an initializer, to setup the case where before or after are nil. Silly me.
I never use JS and Apollo so I cannot help with that. I just use Axios to send a request for data to my App, and my App returns data. I think Apollo is for fancy smancy Apps way beyond anything I would use with Shopify. It seems nice though. I guess once you get used to it as a thing, it makes sense to use it. I just hate adding an extra 100,000 lines of someone else's JS to a project, and trying to keep up with those Joneses. It is hard enough just keeping up with the basic CRUD.
Oops. I get it now. Apollo would be used by people using JS to do their GraphQL calls. Since I would never use JS for my server, that is probably why I never use Apollo!
Great answer, gives me a solid direction where to start on pagination. Ran into a hurdle though; before and after in the products query cannot be null, so I can't set my before or after to null until I've clicked my button for next page. Perhaps there's a clever solution to this? My application is unhappy with other attempts of mine.
The before and after can in fact be nil, and if they are, it does not affect results. So you can call the correct routine in your code, based on knowing where you're at. It is really unfortunate that once again, crap-ass Javascript ruins our good days by being counter intuitive and using nil where we use null or weirdCaseSituations where we might use weird_case_situations, because, well, it was expelled from one guy's brain in the early '90's and has yet to be fixed to be compatible with anything else out there.
Thanks for the quick response, shortly after I wrote my reply I figured out that by removing the `!` from the variable definition it's no longer a required value. My code was `$beforeItemCursor: String!` where it was a non-nullable argument because of the `!`. I've since removed it and it can now be null: `$beforeItemCursor: String`.