The last topic that we’ll cover in this tutorial is
pagination. We’ll implement a simple pagination approach so
that users are able to view the links in smaller chunks
rather than having an extremely long list of Link
elements.
Once more, we first need to prepare the React components for
this new functionality. In fact, we’ll make a slight
adjustment to the current routing setup. The idea is that
the LinkList
component will be used for two different
purposes (and routes). The first one is to display the top
ten voted links and the second use case is to display new
links in a list separated into multiple pages that the user
can navigate through.
Let’s be sure to import the Navigate
component so we don’t
get any errors.
We’ve now added two new routes: /top
and /new/:page
. The
latter reads the value for page
from the url so that this
information is available inside the component that’s
rendered. For this route that’s LinkList
.
The main route /
now redirects to the first page of the
route where new posts are displayed.
Before moving on, quickly add a new navigation item to the
Header
component that brings the user to the /top
route.
We also need to add some logic to the LinkList
component
to account for the two different responsibilities it now
has.
The query now accepts arguments that we’ll use to implement
pagination and ordering. skip
defines the offset where
the query will start. For example, if we passed a value of
10 for this argument, it means that the first 10 items
of the list will not be included in the response. take
then defines the limit or how many elements we want to
load from that list. If we pass in 10
for skip
and 5
for take
, we’ll receive items 10 to 15 from the list.
orderBy
defines how the returned list should be sorted.
But how can we pass the variables when using the useQuery
hook which is fetching the data under the hood? The key is
that we need to pass these variables in when we make the
call to useQuery
.
We use the useLocation
hook to get the current pathname of the page being visited.
We’re passing in an object as the second argument to
useQuery
, right after we pass in the FEED_QUERY
document. We can use this object to modify the behavior of
the query in various ways. One of the most common things we
do with it is to provide variables
.
const getQueryVariables = (isNewPage, page) => {
const skip = isNewPage ? (page - 1) * LINKS_PER_PAGE : 0;
const take = isNewPage ? LINKS_PER_PAGE : 100;
const orderBy = { createdAt: 'desc' };
return { take, skip, orderBy };
};
We’re now passing take, skip, orderBy
values as
variables
based on the current page.
Also note that we’re including the ordering attribute
{ createdAt: 'desc' }
for the new
page to make sure the
newest links are displayed first. The ordering for the
/top
route will be calculated manually based on the number
of votes for each link.
We also need to define the LINKS_PER_PAGE
constant and
then import it into the LinkList
component.
Next, we need functionality for the user to switch between
the pages. First add two button
elements to the bottom of
the LinkList
component that can be used to navigate back
and forth.
Since the setup is slightly more complicated now, we are going to calculate the list of links to be rendered in a separate method.
For the /new
route, we simply return all the links returned
by the query. That’s logical since here we don’t have to
make any manual modifications to the list that is to be
rendered. If the user loaded the component from the /top
route, we’ll sort the list according to the number of votes
and return the top 10 links.
Let’s have a closer look at the logic for the Next and Previous links.
{
isNewPage && (
<div className="flex ml4 mv3 gray">
<div
className="pointer mr2"
onClick={() => {
if (page > 1) {
navigate(`/new/${page - 1}`);
}
}}
>
Previous
</div>
<div
className="pointer"
onClick={() => {
if (page <= data.feed.count / LINKS_PER_PAGE) {
const nextPage = page + 1;
navigate(`/new/${nextPage}`);
}
}}
>
Next
</div>
</div>
);
}
We start by retrieving the current page from the URL and
doing a sanity check to make sure that it makes sense to
paginate back or forth. We then calculate the next page and
tell the router where to navigate to next. The router will
then reload the component with a new page
in the URL that
will be used to calculate the right chunk of links to load.
Run the app by typing yarn start
in a terminal and use the
new buttons to paginate through the list of links!
Through the changes that we made to the FEED_QUERY
, we’ll
notice that the update
functions of the mutations don’t
work any more. That’s because readQuery
now also expects
to get passed the same variables that we defined before.
Note:
readQuery
essentially works in the same way as thequery
method on theApolloClient
that we used to implement the search. However, instead of making a call to the server, it will simply resolve the query against the local store! If a query was fetched from the server with variables,readQuery
also needs to know the variables to make sure it can deliver the right information from the cache.
We have now added a simple pagination system to the app, allowing users to load links in small chunks instead of loading them all up front.