Pubudu Jayasanka
Pubudu Jayasanka

Reputation: 1462

How react Infinite Queries support with offset and limit

I tried to implement an infinite scroll with my react.js project using Infinite Queries in react-query and react-virtual. But Infinite query is supporting with the cursor and the page. and my API is not supported for pages, and it has a limit, offset, and totalCount in the metaData as below

meta: { limit: 100, offset: 0, total: 1000}

Are Infinite Queries support for limit and offset?

There are links that I followed.

https://codesandbox.io/s/github/tannerlinsley/react-virtual/tree/master/examples/infinite-scroll

https://react-query.tanstack.com/docs/guides/infinite-queries

Upvotes: 6

Views: 6892

Answers (2)

aminos
aminos

Reputation: 1

Requesting Spotify API to fetch artist's album, with total, limit & offset.

import React, { useEffect, useState } from 'react';
import { useInfiniteQuery } from '@tanstack/react-query';
import { useInView } from 'react-intersection-observer';
import './App.css';
import { SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SEC } from './constants';

export default function App() {
  const { ref, inView } = useInView();
  const [count, setCount] = useState<number>(0);

  const LIMIT = 10;

  const getSpotifyAccessToken = async () => {
    const encodedToken = btoa(`${SPOTIFY_CLIENT_ID}:${SPOTIFY_CLIENT_SEC}`);

    const res = await fetch('https://accounts.spotify.com/api/token', {
      method: 'POST',
      headers: {
        Authorization: `Basic ${encodedToken}`,
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: new URLSearchParams({
        grant_type: 'client_credentials',
      }),
    });
    const data = await res.json();

    // Sidhu:  4PULA4EFzYTrxYvOVlwpiQ
    // Arijit: 4YRxDV8wJFPHPTeXepOstw

    const response = await fetch(`https://api.spotify.com/v1/artists/4PULA4EFzYTrxYvOVlwpiQ/albums?album_type=SINGLE&offset=${count * LIMIT}&limit=${LIMIT}`, {
      headers: {
        Authorization: `Bearer ${data.access_token}`,
        'Content-Type': 'application/x-www-form-urlencoded',
      }
    })
    const finalData = await response.json()
    return finalData as {
      items: {
        id: string;
        name: string;
        total: number;
        images: {
          height: number;
          width: number;
          url: string;
        }[]
      }[]
      total: number;
    };
  }

  const { data, isLoading, isError, fetchNextPage, isFetchingNextPage } = useInfiniteQuery({
    queryKey: ['items'],
    queryFn: getSpotifyAccessToken,
    initialPageParam: 0,
    getNextPageParam: (lastPage) => Math.ceil(lastPage.total / LIMIT),
  });

  useEffect(() => {
    if (inView) {
      setCount((prev) => prev + 1);
    }
  }, [fetchNextPage, inView])

  useEffect(() => { fetchNextPage() }, [count])

  if (isLoading) {
    return <div>Loading...</div>
  }

  if (isError) {
    return <div>Error</div>
  }

  return (
    <main style={{
      display: "flex",
      flexDirection: "column",
      gap: "10px",
      fontFamily: "cursive"
    }}>
      <h1 style={{
        textAlign: "center"
      }}>Sidhu Moosewala</h1>
      {/* <h1 style={{
        textAlign: "center"
      }}>Arijit Singh</h1> */}
      {data?.pages.map((page) => (
        page.items.map((item) => (
          <div style={{
            display: "flex",
            gap: "20px",
            alignItems: "center",
            padding: "10px",
            backgroundColor: "green",
            borderRadius: "8px",
            cursor: "pointer"
          }} key={item.id}
            onClick={() => window.open(`https://open.spotify.com/album/${item.id}`, "_blank")}
          >
            <img
              width={100}
              height={100}
              style={{
                borderRadius: "5px"
              }}
              src={item.images[0].url}
            />
            <h2 style={{
              color: "white"
            }}>{item.name}</h2>
          </div>
        ))
      ))}
      <div ref={ref}></div>
      {isFetchingNextPage && <div>Loading...</div>}
      <h2 style={{
        textAlign: "center"
      }}>You reached the boundary of infinite scroll, thanks for visiting us! (Sidhu, dil da ni maada..)</h2>
      {/* Pyaar bhare gaane!! */}
    </main>
  )
}

Upvotes: 0

TkDodo
TkDodo

Reputation: 28988

infiniteQueries don't really care how your backend delivers a "cursor" to the next page, it just matters that the backend delivers something.

Basically, whatever is returned from getNextPageParam will be injected into the queryFn as pageParam. Here is an example that might fit your use-case:

const { data } = useInfiniteQuery(
    'key',
    ({ pageParam }) => axios.get(myUrl + '?offset=' + pageParam?.offset ?? 0),
    {
        getNextPageParam: (lastPage) => lastPage?.meta
    }
)

The concept of a "page" is merely what ever your backend returns for a single fetch. So here, on the first fetch (for page 0), we have no pageParam, so we initalize with offset: 0. Then, we fetch with that, and the backend returns the first set of data (a page), and we extract the meta info for it (offset = 20 or whatever).

If you call fetchNextPage(), the queryFn will be called with that meta information, so the next fetch does ?offset=20. The backend delivers offset: 40 as meta and the infinity continues.

Hope that explains it :)

Upvotes: 4

Related Questions