Apollo comes with a simple method
fetchMore() that you can use to implement offset pagination with infinite scroll in
your React application.
Offset pagination is very popular in modern applications due to its convenience: The data is presented as an endless
stream, and you can infinitely scroll without reaching the end (in theory). In this tutorial, we explain how such
pagination can be implemented in a React application with Apollo and
So how such pagination is done? The next short section summarizes what you need to do.
How to implement offset-based pagination with Apollo and React
First, we'd like to remind what the offset-based pagination is. With this kind of pagination, your client application must provide an offset and limit values when querying the server for new items to indicate what entries must be returned and also their quantity. And to implement offset pagination, we use these technologies:
- Apollo Client to use GraphQL in React
react-apolloto use custom React components
apollo-link-state, a data provider and state manager from Apollo
Using the listed technologies, we can create necessary GraphQL resolvers, React components, and an instance of Apollo Client instance in order to implement pagination:
- An instance of Apollo Client with configured
- GraphQL resolvers to be passed to
apollo-link-stateto retrieve the data
- A React component that renders the data and asks for new entries when a condition is met
- A Higher Order Component, also known as a provider, which sends GraphQL queries for new data
Our React application will retrieve chapters from the famous Harry Potter and Hobbit series of books and render them. This is how the end result looks:
If you want to look at the code with our pagination example, check out these two links:
Let's now focus on the implementation.
The Apollo stack comes with
apollo-link-state, a great package that helps to manage the state for our React apps and is used instead of Redux in applications that use GraphQL. Since we don't have a backend API for our demo app, we use
App.js with configured Apollo Client and a basic component:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
As you can see,
apollo-link-state configurations are set in the
clientState property, which points to the resolvers
that retrieve items from the backend API or, in this app, from
ApolloProvider renders the
ChapterListQuery component, which will be sending GraphQL queries.
ChapterListQuery also returns a dumb component
ChapterList to render chapters.
We're now going to create resolvers, and then we'll switch to the React components.
Creating GraphQL resolvers for
apollo-link-state for offset pagination
A minimal configuration object for
apollo-link-state should have two properties:
defaults to return some data by default and
resolvers to return new data.
Have a look at the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
First, we import
mockChapters, an array of chapter objects. Second, we implement the
getChapters() function, which
uses the limit and offset values to slice a chunk of the array and return only the required part.
Pay attention that
apollo-link-state requires that each object has the
__typename property with some value (such as
Chapter) for normalization, which is why each object from
mockChapters is transformed to a new object with this property.
To make sure that our React application renders the first ten chapters once you open it in the browser, we set the
defaults.chapters property to a
Finally, we set the query
chapters to a
getChapters() call with variables. As required by the offset-based pagination implementation, you can pass both limit and offset variables into
getChapters(), although in this implementation we only pass an offset and always use a default limit.
Now we can focus on creating a component that sends a new GraphQL query each time you scroll to the end of the rendered list.
Creating a React component with a GraphQL query
Since it's a bad idea to create components that both render data and send GraphQL queries to the backend, we created
ChapterListQuery, a Higher Order Component that's only concerned with querying the backend. Then we have another React component,
ChapterList, used only for rendering. We'll look at
ChapterList in the next section.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
There are several key aspects that you need to pay attention to in the code snippet above:
GET_CHAPTERS query with
Here's the GraphQL query:
1 2 3 4 5 6 7 8
GET_CHAPTERS is a typical GraphQL query that uses the
gql method from Apollo. Notice that we specified these three values:
limit, the number of items that the resolver must send in one go.
offset, a pointer to the item starting from which the resolver will retrieve data. Offset equals the length of the previous data.
@client, a directive needed only for
apollo-link-state. It tells Apollo that the data must be resolved from the cache and that making a network request isn't necessary.
This GraphQL query will send the limit and offset values to the resolver so that the resolver understands how much data it should return.
Query wrapper component with the
We use a
Query component (kindly presented by
react-apollo) to access data from our component and, importantly, to
fetchMore() method to fetch new data when necessary.
ChapterListQuery returns a new component,
ChapterList, and passes it two props attributes:
1 2 3 4 5 6 7 8 9 10 11
What's important to successfully implement offset-based pagination is to add the
onLoadMore() function, which is shown
1 2 3 4 5 6 7 8 9 10 11 12 13
As you can see,
onLoadMore() calls the Apollo
fetchMore() method, which gets returned by a GraphQL query and is
available thanks to
fetchMore() receives an object parameter with two properties. First, we must pass the variables that the resolver will
use to slice the next part of the array. We pass only an
offset value, which always equals the length of the current array. You can also pass limit if it's necessary for your React app.
The other property inside
updateQuery(), a function that accepts two parameters —
prev contains the data returned by the previous query, and
updateQuery() changes the result of the current query on the fly via
Object.assign(). Why is it necessary? Consider this: We need each GraphQL query to return only a part of items array for offset-based pagination. Our React app, however, displays not only the items just returned, but all the items including the ones loaded before. Hence, we must merge the current array loaded to our React application with its next part in
You can look at it as you first cut the original array
mockChapters in parts and then re-create it chunk by chunk in
the React app.
That's it for
ChapterListQuery, and we can review the
ChapterList component with infinite scroll
We need a component to display the list of chapters with a scroll bar. This component,
ChapterList, receives items
onLoadMore() function to load more data. And whenever we scroll to the bottom of the list,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
First, we created a function
handleScroll() that gets called whenever the
scroll event happens.
handleScroll() calculates whether it's time to load new chapters by simply analyzing if you scrolled until the last item in the list. And if you did,
onLoadMore() gets called and, provided there are other items, a GraphQL query is sent.
The entire flow in our React application with pagination created with Apollo is this:
ChapterListgets rendered and runs
onLoadMore()when the list is scrolled to the bottom.
ChapterListQuerysends a GraphQL query, which gets resolved by
apollo-link-state(no HTTP request is actually sent).
apollo-link-stateuses resolvers to slice a chunk of requested items and respond to the GraphQL query.
- New chapters are returned and
fetchMore()gets called to add the latest chapters.
- React re-renders the application to show new items.
That's how you implement offset-based pagination with infinite scroll in React using Apollo and