skip to Main Content

WordFlix – Single Page App tutorial: Step 10 – GraphQL Pagination

Post Series: WordFlix – Super Powered Single Page App Tutorial

Step 10 – Starting Point

  • If you followed the previous steps of the tutorial, you should be able to continue here, but if you’re just jumping in or got lost in the last steps or just want to skip ahead, you can either check-out this branch of the Github repo or change directories to the “9” theme (wp-content/themes/9)

Add Pagination to the MoviesPage

In this step, we add pagination to our MoviesPage.

First, we create a LoadMore styled component, which will serve as a button that can be clicked to load more movies.

/**
 * Create a styled div used for loading more items
 */
const LoadMore = styled.div`
  background:#ad0000;
  color:white;
  border-radius:25px;
  text-align:center;
  font-size: 25px;
  line-height: 40px;
  padding: 10px 20px;
  font-family:TTFirsMedium;
  margin: 0 auto 50px;
  &:hover{
    background:#0087be;
  }
  display:inline-block;
  cursor:pointer;
`;

Then we add the loadMore button to our MoviesPage component. Below the </Row> let’s add the following:

<Row>
  {this.loadMoreButton(movies.pageInfo, loadMoreEntries)}
</Row>

This adds a new row below the Row where our movie grid is rendered. And inside this Row, we call the loadMoreButton method on this component. So, let’s create that method.

Add the loadMoreButton method

Inside the MoviePage component, before the render method, add the following:

loadMoreButton( pageInfo, loadMoreEntries ) {
  if ( pageInfo.hasPreviousPage ) {
    return ( <LoadMore onClick={ loadMoreEntries }>Load More</LoadMore> );
  }
}

This method takes in pageInfo and loadMoreEntries, and returns the LoadMore button if the pageInfo indicates there is a previousPage. And if there is, the LoadMore button calls “loadMoreEntries” on click.

Update our GraphQL query to get pageInfo

Now, let’s modify our GraphQL query to ask for pageInfo so we know if there is another page of data or not.

Our query should now look like this:

const moviesQuery = gql`
  query moviesList($cursor: String) {
    movies:posts(last: 20, before:$cursor) {
      pageInfo {
        startCursor
        hasPreviousPage
      }
      edges {
        movie:node {
          id
          title
          poster:featuredImage {
            url:sourceUrl
          }
        }
      }
    }
  }
`;

The query now has a variable “$cursor” and uses that cursor value in the “before” argument of the “posts” query.

Update Apollo to support pagination

So now we need to ensure the cursor is used by Apollo.

Update our MoviesPageWithData like so:

const MoviesPageWithData = graphql(moviesQuery, {

  props({
    data: {
      loading,
      movies,
      fetchMore
    }
  }) {

    /**
     * Return the props to connect to the component
     */
    return {
      data: {
        loading,
        movies,
        fetchMore,
        loadMoreEntries: () => {
          return fetchMore({
            query: moviesQuery,
            variables: {
              cursor: movies.pageInfo.startCursor
            },
            updateQuery: (previousResult, {fetchMoreResult}) => {

              /**
               * Pluck the new edges out of the query results
               */
              const newEdges = fetchMoreResult.movies.edges;

              /**
               * Pluck the new pageInfo out of the query results
               */
              const pageInfo = fetchMoreResult.movies.pageInfo;

              /**
               * Return the movies with the new edges merged with the existing ones, and
               * the new pageInfo replacing the old pageInfo
               */
              return {
                movies: {
                  edges: [...previousResult.movies.edges, ...newEdges],
                  pageInfo,
                },
              };
            },
          });
        }
      }
    }
  },

})(MoviesPage);

This is a lot to take in, so let’s break it down.

We’re connecting our MoviesPage component with our moviesQuery like before, but this time we’re adjusting the data that Apollo connects to the component.

We take in the existing data:

props({
    data: {
      loading,
      movies,
      fetchMore
    }
  }) {

Then we return a new shape of data, this time, including a loadMoreEntries function.

The loadMoreEntries function is now available by our MoviesPage component to be called, and when called, it runs the method “fetchMore” and queries for more data, using the pageInfo.startCursor as the value for the $cursor variable.

Then, when data is returned to Apollo, we take the results and we replace any existing pageInfo with the new pageInfo, and we merge the newEdges with the existing edges, effectively updating our list of movies.

return {
  movies: {
    edges: [...previousResult.movies.edges, ...newEdges],
    pageInfo,
  },
}

And our connected components automatically re-render when their connected data changes.

So now, we have added pagination to our app!

Leave a Reply

Share This
Back To Top