Tutorial: How to implement infinite loading with URQL GraphQL
Jasser Mark Arioste
So you're tasked with implementing an infinite loading pattern on mobile using a graphql schema. Let me share my knowledge on how to implement infinite loading on URQL on a Next.js Project. In this tutorial, I'll be showing you on how to implement infinite loading on URQL with rick and morty api as our backend endpoint
Infinite loading a is very common pattern in mobile layout since it's optimized for mobile UX as opposed to pagination which is more suited for desktop. There are many ways to implement infinite loading in URQL however today I'm going to show to you probably the most simple pattern to implement infinite loading pattern using a custom hook.
How does and infinite loading pattern work? #
For infinite loading, we have to create a custom hook that tracks the following: the page number and the new items from graphql cache. Every time the page changes, we have to update the items displayed.
Step 1: Project Setup #
Please see the tutorial on how to setup URQL with Next.js as we'll continue from that tutorial. It also contains setup for the rick and morty api and the episodes list that we'll be using so don't skip that part! Once you finish the setup, proceed to the next step.
Step 2: Modifying the Episodes Query #
Let's modify the episodes query in episodes.graphql
by adding information on the next page
#episodes.graphql query Episodes($page: Int) { episodes(page: $page) { results { air_date created episode name } #Add this part info { next } } }
12345678910111213141516
Run yarn gen
to regenerate the graphql types.
Step 3: Creating the Custom Hook #
Lets create the file first:
#create the hooks folder in the root project directory for all our custom hooks mkdir hooks && cd hooks touch useLoadEpisodes.tsx
123
Insert the code:
//useLoadEpisodes.tsx import { useEffect, useState } from "react"; import { useEpisodesQuery } from "../graphql/episodes.gql"; import { Episode } from "../graphql/types"; export const useLoadEpisodes = () => { const [items, setItems] = useState<Partial<Episode | null>[]>([]); const [page, setPage] = useState(1); const [{ data, fetching }] = useEpisodesQuery({ variables: { page: page, }, }); const episodes = data?.episodes?.results ?? []; //this is the important part, whenever the episodes changes, we concatenate it to the previous list useEffect(() => { setItems(items.concat(episodes)); }, [episodes]); const next = data?.episodes?.info?.next; //when load more is called, we modify the page so that it triggers another query to the backend. const loadMore = () => { if (next) { setPage(next!); } }; //return the important items for our interface return { items, hasNext: Boolean(next), loadMore, fetching, }; };
12345678910111213141516171819202122232425262728293031323334353637383940
Step 4: Implement infinite loading in pages/index.tsx
#
Inside pages/index.tsx
, we'll make a couple of changes to take into account infinite loading:
//pages/index.tsx import type { NextPage } from "next"; import { useLoadEpisodes } from "../hooks/useLoadEpisodes"; const Home: NextPage = () => { //use the custom hook instead of the generated hook const { items, hasNext, loadMore, fetching } = useLoadEpisodes(); const episodes = items; return ( <div className="container"> {/* this stays the same */} <ol className="episode-list"> {episodes?.map((episode, i) => { return ( <li key={i} className="episode"> <h2 className="episode-title">{episode?.name}</h2> <p className="episode-ep">{episode?.episode}</p> <time dateTime={episode?.air_date ?? ""}> {episode?.air_date} </time> </li> ); })} </ol> {/* add loading indicator for when we are fetching data from the api */} {fetching && <p>Loading...</p>} {/* add a load more button and call loadMore when clicked */} {/* hide it there are no next pages */} {!fetching && hasNext && ( <button className="load-more" onClick={loadMore}> Load more </button> )} </div> ); }; export default Home;
12345678910111213141516171819202122232425262728293031323334353637383940
Bonus Step: Style and Refactor your components #
I'll leave this step up to you since it's out of the scope of this tutorial. However, you may check the completed code and the demo below:
Full Code: https://github.com/jmarioste/setup-urql-nextjs/tree/infinite-loading-2
Demo: https://setup-urql-nextjs-git-infinite-loading-2-jmarioste.vercel.app/
Resources #
URQL Docs - https://formidable.com/open-source/urql/docs/
Credits
- Photo by Rafael Cerqueira from Pexels