Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Sergio Daniel Xalambrí
Sergio Daniel Xalambrí

Posted on • Edited on • Originally published atsergiodxa.com

     

Using Paginated Data with SWR

Originally published athttps://sergiodxa.com/articles/swr/pagination/

In aprevious article we build a Next.js application with SWR to fetch data from thePokeapi. However, our API gives us paginated data so we couldn't show all the Pokémon at the same time and it was limited to the first 20 one.

Let's modify it to build add infinite scroll to it and show all possible
Pokémon.

Running Demo

This is the final project running in CodeSandbox

Introducing useSWRPages

Along withuseSWR, the SWR library gives us auseSWRPages hook which lets us do paginated data. The way it works is we pass a key for cache the whole list, in our case, it will bepokemon-list.

And we pass an inlined React component, this component will receive anoffset and awithSWR function, here we will pass the results ofuseSWR towithSWR, then we could check if we don't have data to show a loading message or if we already have the data and return an array of elements.

importHeadfrom"next/head";importuseSWR,{useSWRPages}from"swr";importfetcherfrom"../lib/fetcher";importPokemonShortfrom"../components/pokemon-short";functionHomePage(){const{pages,isLoadingMore,loadMore}=useSWRPages("pokemon-list",({offset,withSWR})=>{consturl=offset||"https://pokeapi.co/api/v2/pokemon";const{data}=withSWR(useSWR(url,fetcher));if(!data)returnnull;const{results}=data;returnresults.map(result=>(<PokemonShortkey={result.name}name={result.name}/>));},SWR=>SWR.data.next,[]);return(<><Head><linkhref="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css"rel="stylesheet"/></Head><sectionclassName="container mx-auto"><divclassName="-mx-2 flex flex-wrap">{pages}</div></section></>);}exportdefaultHomePage;
Enter fullscreen modeExit fullscreen mode

This hook returns a keypages where it the elements of all of our pages, this is what we need to render. We also have anisLoadingMore key to know when we are in the process of fetching more data from our API.

There is anotherisReachingEnd to know when we don't have more things to load so we could do something in the UI, e.g. stop rendering a button.

Manually Load More

Let's start implementing our load more using a manual click on a button.

importReactfrom"react";importHeadfrom"next/head";importuseSWR,{useSWRPages}from"swr";importfetcherfrom"../lib/fetcher";importPokemonShortfrom"../components/pokemon-short";functionHomePage(){const{pages,isLoadingMore,loadMore}=useSWRPages("pokemon-list",({offset,withSWR})=>{consturl=offset||"https://pokeapi.co/api/v2/pokemon";const{data}=withSWR(useSWR(url,fetcher));if(!data)returnnull;const{results}=data;returnresults.map(result=>(<PokemonShortkey={result.name}name={result.name}/>));},SWR=>SWR.data.next,[]);return(<><Head><linkhref="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css"rel="stylesheet"/></Head><sectionclassName="container mx-auto"><divclassName="-mx-2 flex flex-wrap">{pages}</div><divclassName="mx-auto mt-10 mb-20 w-1/3"><buttonclassName="bg-red-600 border-solid border-2 hover:bg-white border-red-600 text-white hover:text-red-600 font-bold py-2 px-4 rounded-full w-full"disabled={isLoadingMore}onClick={loadMore}>LoadMorePokémon</button></div></section></>);}exportdefaultHomePage;
Enter fullscreen modeExit fullscreen mode

Here our new button will trigger theloadMore functionuseSWRPages gives us to trigger a new fetch. We also use theisLoadingMore more to disable the button while we are loading.

Infinite Scroll

Now let's implement infinite scroll, we will use our button to detect if we reached the end and callloadMore.

First, we need to detect if we are near the button, this is possible using the IntersectionObserver API, let's create a hook to use it.

importReactfrom"react";functionuseOnScreen(ref,rootMargin="0px"){const[isIntersecting,setIntersecting]=React.useState(false);React.useEffect(()=>{constobserver=newIntersectionObserver(([entry])=>setIntersecting(entry.isIntersecting),{rootMargin});if(ref.current){observer.observe(ref.current);}return()=>{observer.unobserve(ref.current);};},[]);returnisIntersecting;}exportdefaultuseOnScreen;
Enter fullscreen modeExit fullscreen mode

Now let's update our HomePage to use it, we will also create an effect to runloadMode if our hook returns true.

importReactfrom"react";importHeadfrom"next/head";importuseSWR,{useSWRPages}from"swr";importfetcherfrom"../lib/fetcher";importPokemonShortfrom"../components/pokemon-short";importuseOnScreenfrom"../hooks/use-on-screen";functionHomePage(){const{pages,isLoadingMore,loadMore}=useSWRPages("pokemon-list",({offset,withSWR})=>{consturl=offset||"https://pokeapi.co/api/v2/pokemon";const{data}=withSWR(useSWR(url,fetcher));if(!data)returnnull;const{results}=data;returnresults.map(result=>(<PokemonShortkey={result.name}name={result.name}/>));},SWR=>SWR.data.next,[]);const$loadMoreButton=React.useRef(null);constisOnScreen=useOnScreen($loadMoreButton,"200px");React.useEffect(()=>{if(isOnScreen)loadMore();},[isOnScreen]);return(<><Head><linkhref="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css"rel="stylesheet"/></Head><sectionclassName="container mx-auto"><divclassName="-mx-2 flex flex-wrap">{pages}</div><divclassName="mx-auto mt-10 mb-20 w-1/3"><buttonref={$loadMoreButton}className="bg-red-600 border-solid border-2 hover:bg-white border-red-600 text-white hover:text-red-600 font-bold py-2 px-4 rounded-full w-full"disabled={isLoadingMore}onClick={loadMore}>LoadMorePokémon</button></div></section></>);}exportdefaultHomePage;
Enter fullscreen modeExit fullscreen mode

Infinite Scroll Triggered by Click

This is nice, but maybe the user doesn't want to start doing infinite scroll right away, instead, we could wait for the first click on the button and then initialize the infinite scroll, we could even limit the amount of infinite scrolls we want and expect the user to click again to confirm.

importReactfrom"react";importHeadfrom"next/head";importuseSWR,{useSWRPages}from"swr";importfetcherfrom"../lib/fetcher";importPokemonShortfrom"../components/pokemon-short";importuseOnScreenfrom"../hooks/use-on-screen";functionHomePage(){const{pages,isLoadingMore,loadMore}=useSWRPages("pokemon-list",({offset,withSWR})=>{consturl=offset||"https://pokeapi.co/api/v2/pokemon";const{data}=withSWR(useSWR(url,fetcher));if(!data)returnnull;const{results}=data;returnresults.map(result=>(<PokemonShortkey={result.name}name={result.name}/>));},SWR=>SWR.data.next,[]);const[infiniteScrollEnabled,setInfiniteScrollEnabled]=React.useState(false);const$loadMoreButton=React.useRef(null);constinfiniteScrollCount=React.useRef(0);constisOnScreen=useOnScreen($loadMoreButton,"200px");React.useEffect(()=>{if(!infiniteScrollEnabled||!isOnScreen)return;loadMore();constcount=infiniteScrollCount.current;if(count+1===3){setInfiniteScrollEnabled(false);infiniteScrollCount.current=0;}else{infiniteScrollCount.current=count+1;}},[infiniteScrollEnabled,isOnScreen]);return(<><Head><linkhref="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css"rel="stylesheet"/></Head><sectionclassName="container mx-auto"><divclassName="-mx-2 flex flex-wrap">{pages}</div><divclassName="mx-auto mt-10 mb-20 w-1/3"><buttonref={$loadMoreButton}className="bg-red-600 border-solid border-2 hover:bg-white border-red-600 text-white hover:text-red-600 font-bold py-2 px-4 rounded-full w-full"disabled={isLoadingMore}onClick={()=>{loadMore();setInfiniteScrollEnabled(true);}}>LoadMorePokémon</button></div></section></>);}exportdefaultHomePage;
Enter fullscreen modeExit fullscreen mode

Here we have a state to know if the infinite scroll is enabled and a ref to count how many times we have loaded more using infinite scroll. Once we did it three times we disable infinite scroll and reset the count, then the user can click the button again to enable it back.

Final Words

With this, we added pagination support to our application using SWR, note how the SWR specific code was created at the beginning and we didn't change it again, the rest of our code was special for our application in how we want the user experience to work and not in how we should handle data fetching.

This is one of the best features of SWR, you only need to care about what makes your application special and not generic things like data fetching.

In the next article, we will continue adding more features to our Pokedex
application.

Top comments(3)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
stunaz profile image
stunaz
  • Location
    4
  • Joined
• Edited on• Edited

Hi, curious to understand whyuseSWRPages event exists? couldn't we simply useuseSWR and pass different key (full url with pagination request param i.e. &offset=... or ?page=?&limit=) ... is there a real benefit (perf, caching...) for usinguseSWRPages or it 's only a nice shortcut?

CollapseExpand
 
sergiodxa profile image
Sergio Daniel Xalambrí
  • Email
  • Location
    Lima, Perú
  • Work
    Web Developer at Daffy.org
  • Joined

If you use the same useSWR with different keys you are going to replace the current page with the new one, that works great if you only show a single page at the time, useSWRPages is used to implement paginated infinite scroll.

It also comes with the nice feature of keeping the amount of pages already loaded in cache, so the next time the user visits a page it will render the same amount of times instead of only the first page, this works great when going back to the previous page and helps keep the scroll in the same position the user was before navigating.

CollapseExpand
 
stunaz profile image
stunaz
  • Location
    4
  • Joined

Nice! thank you very much for that

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

  • Location
    Lima, Perú
  • Work
    Web Developer at Daffy.org
  • Joined

More fromSergio Daniel Xalambrí

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp