Paginação no React Query
Fala Dev Doido!! Já sofreu com paginação? Seus problemas acabaram. No React Query o jeito mais simples é o seguinte:
const result = useQuery(['projects', page], fetchProjects)
Mas claro que o mais simples nem sempre é o ideal... Se você rodar esse código notará que o state da query fica alternando entre os states de sucesso e loading. Isso porque cada página neste caso é tratada como uma nova query.
E agora Dev Doido?
Calma gente, ainda há salvação. O pessoal da lib React Query sabia disso e criou uma prop específica pra paginação chamada keepPreviousData
. É só colocar ela no objeto config como true e pronto. Veja o exemplo abaixo:
import { useQuery } from "react-query";
import { useState } from "react";
export default function PageExample() {
const [page, setPage] = useState(1);
const [breweries, setBreweries] = useState([]);
const fetchBreweries = (newPage = 1) =>
fetch("https://api.openbrewerydb.org/breweries?page=" + newPage)
.then((res) => res.json())
.then((resJson) => setBreweries((prev) => [...prev, ...resJson]));
const { isLoading, isError, error, data, isFetching, isPreviousData } = useQuery(
["breweries", page],
() => fetchBreweries(page),
{ keepPreviousData: true },
);
return (
<div>
{isLoading ? (
<div>Loading...</div>
) : isError ? (
<div>Error: {error.message}</div>
) : (
<div>
{breweries.map((project) => (
<p key={project.id}>{project.name}</p>
))}
</div>
)}
<span>Current Page: {page + 1}</span>
<button
onClick={() => setPage((old) => Math.max(old - 1, 0))}
disabled={page === 0}
>
Previous Page
</button>{" "}
<button
onClick={() => {
if (!isPreviousData) {
setPage((old) => old + 1);
}
}}
// Disable the Next Page button until we know a next page is available
disabled={isPreviousData}
>
Next Page
</button>
{isFetching ? <span> Loading...</span> : null}
</div>
);
}
Hook useInfiniteQuery
Temos também a hook useInfiniteQuery
pra fazer esse trabalho de paginação.
Quando usamos ela, notamos que coisas diferentes acontecem, como por exemplo:
data
agora é um objeto contendo dados infinitos.data.pages
é um array contendo cada página carregada.data.pageParams
é um array contendo os parâmetros usados pela paginação para carregar essas páginas.- As funções
fetchNextPage
efetchPreviousPage
agora existem. - As opções
getNextPageParam
egetPreviousPageParam
agora estão disponíveis para determinar se existem mais dados para serem carregados e a informação contida neles. Essa informação é fornecida como um parâmetro adicional na query function ( que pode ser sobrescrito opcionalmente pelo override das funçõesfetchNextPage
efetchPreviousPage
). - Agora temos um booleano chamado
hasPreviousPage
tendo valortrue
segetPreviousPageParam
retornar algum valor. - Os booleanos
isFetchingNextPage
eisFetchingPreviousPage
agora estão disponíveis e podem ser identificados entre um refresh feito em segundo plano e um carregamento de mais dados.
import { useInfiniteQuery } from "react-query";
export default function PageExample() {
const fetchBreweries = ({ pageParam = 1 }) =>
fetch("https://api.openbrewerydb.org/breweries?page=" + pageParam)
.then((res) => res.json())
.then((res) => {
return { results: res, next: pageParam + 1, prev: pageParam - 1 };
});
const {
data,
error,
fetchNextPage,
hasNextPage,
isFetching,
isFetchingNextPage,
status,
} = useInfiniteQuery("breweries", fetchBreweries, {
getNextPageParam: (lastPage, pages) => lastPage.next,
getPreviousPageParam: (firstPage, pages) => firstPage.prev,
});
return status === "loading" ? (
<p>Loading...</p>
) : status === "error" ? (
<p>Error: {error.message}</p>
) : (
<>
{data.pages.map((group, i) => (
<>
{group.results.map((project) => (
<p key={project.id}>{project.name}</p>
))}
</>
))}
<div>
<button
onClick={() => {
fetchNextPage();
}}
disabled={!hasNextPage || isFetchingNextPage}
>
{isFetchingNextPage
? "Loading more..."
: hasNextPage
? "Load More"
: "Nothing more to load"}
</button>
</div>
<div>{isFetching && !isFetchingNextPage ? "Fetching..." : null}</div>
</>
);
}
Neste exemplo podemos ver uma API pública, em que retornamos os dados e na propriedade next
indicamos se temos mais dados a serem carregados, incrementando sempre o número da página. Mesma coisa vemos na propriedade prev
pra indicar se existe uma página anterior.
Bom por hoje é só pessoal, até mais!