Como implementar a rolagem infinita e a paginação com Next.js e TanStack Query
A maioria das aplicações que irá desenvolver irá gerir dados; à medida que os programas continuam a ser escalados, pode haver uma quantidade cada vez maior desses dados. Quando as aplicações não conseguem gerir grandes quantidades de dados de forma eficaz, o seu desempenho é fraco.
A utilização da paginação e do scrolling infinito são métodos práticos para otimizar a eficiência das aplicações, permitindo um melhor tratamento da apresentação de dados e, simultaneamente, melhorando a experiência geral do utilizador.
Paginação e rolagem infinita usando o TanStack Query
O TanStack Query - uma adaptação do React Query - é uma biblioteca robusta de gerenciamento de estado para aplicativos JavaScript. Ela oferece uma solução eficiente para gerenciar o estado da aplicação, entre outras funcionalidades, incluindo tarefas relacionadas a dados, como cache.
A paginação é um método de organização de vastos conjuntos de dados, dividindo-os em secções mais pequenas e facilmente navegáveis através da utilização de controlos de paginação. Por outro lado, o deslocamento infinito oferece uma abordagem de navegação adaptativa em que, à medida que o utilizador se desloca para baixo, a informação adicional é carregada e apresentada sem problemas, evitando assim a necessidade de navegação direta.
A paginação e a deslocação infinita são ambos métodos de gestão e apresentação de volumes substanciais de informação de uma forma fácil de utilizar, cada um com as suas próprias vantagens e desvantagens, dependendo das necessidades específicas de uma aplicação.
Pode encontrar o código fonte deste projeto no repositório GitHub designado para o mesmo.
Configurando um projeto Next.js
Para iniciar o processo, estabeleça um projeto Next.js instalando sua iteração mais recente, a versão 13, que emprega o uso de um diretório “App” como base.
npx create-next-app@latest next-project --app
Para prosseguir, terá de instalar o pacote TanStack no seu projeto utilizando o npm, um gestor de pacotes amplamente utilizado para aplicações Node.js.
npm i @tanstack/react-query
Integrar o TanStack Query na aplicação Next.js
Para incorporar o TanStack Query no seu projeto Next.js, é necessário estabelecer e inicializar uma nova instância do TanStack Query no núcleo da aplicação - especificamente, no ficheiro layout.js. Isso pode ser feito importando o QueryClient e o QueryClientProvider do TanStack Query. Posteriormente, envolva a propriedade da criança com QueryClientProvider, estruturado como mostrado abaixo:
"use client"
import React from 'react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
};
export default function RootLayout({ children }) {
const queryClient = new QueryClient();
return (
<html lang="en">
<body>
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
</body>
</html>
);
}
export { metadata };
Esta configuração concede autoridade total ao TanStack Query para compreender e manipular o estado atual do software.
Implementar a paginação utilizando o gancho useQuery
A utilização do gancho useQuery
facilita a recuperação e o tratamento eficientes dos dados, incorporando atributos de paginação, incluindo números de página, para obter facilmente porções de informação direccionadas.
O Advanced Repeat é altamente versátil e oferece uma série de opções para adaptar o processo de recuperação de dados de acordo com os seus requisitos específicos. Isto inclui a capacidade de estabelecer definições de cache e gerir condições de carregamento com facilidade, resultando num resultado de paginação suave e unificado.
Para incorporar a paginação na nossa aplicação Next.js, vamos criar um ficheiro Pagination/page.js situado no diretório “src/app” do código fonte. Este ficheiro específico conterá as instruções de importação necessárias para implementar a funcionalidade de paginação.
"use client"
import React, { useState } from 'react';
import { useQuery} from '@tanstack/react-query';
import './page.styles.css';
Em seguida, defina um componente funcional React. Dentro desse componente, você precisa definir uma função que buscará dados de uma API externa. Nesse caso, use a API JSONPlaceholder para buscar um conjunto de posts.
export default function Pagination() {
const [page, setPage] = useState(1);
const fetchPosts = async () => {
try {
const response = await fetch(`https://jsonplaceholder.typicode.com/posts?
_page=${page}&_limit=10`);
if (!response.ok) {
throw new Error('Failed to fetch posts');
}
const data = await response.json();
return data;
} catch (error) {
console.error(error);
throw error;
}
};
// add the following code here
}
O gancho useQuery
pode ser definido especificando um objeto com vários pares de valores chave que representam os vários parâmetros necessários para buscar dados de uma API GraphQL. Esses parâmetros podem incluir a própria string de consulta, variáveis para personalizar a consulta, opções para controlar como os dados são obtidos e atualizados e políticas de repetição em caso de erros de rede ou outros problemas. Ao fornecer esses parâmetros no contexto do gancho useQuery
, permitimos que a árvore de componentes React gerencie o estado associado ao ciclo de vida da consulta, permitindo atualizações mais eficientes e experiências de usuário mais suaves.
const { isLoading, isError, error, data } = useQuery({
keepPreviousData: true,
queryKey: ['posts', page],
queryFn: fetchPosts,
});
A propriedade “keepPreviousData” é definida como “true”, permitindo assim a retenção de dados anteriores ao adquirir novas informações. A variável “queryKey” inclui uma lista de chaves, incluindo o ponto final necessário e o número de página pretendido. Por fim, a invocação da função “fetchPosts” serve de gatilho para a aquisição de dados.
Numa discussão anterior, abordámos o facto de o hook oferecer vários estados que podem ser desconstruídos de forma análoga à dissecação de arrays e objectos. Ao utilizar estes estados, torna-se possível melhorar a experiência global do utilizador através da apresentação de interfaces adequadas durante a fase de recuperação de dados. Entre o conjunto de opções disponíveis estão ‘isLoading’, ‘isError’ e várias outras.
Para apresentar vários ecrãs de mensagens em função do estado atual do procedimento em curso, é necessário incorporar o trecho de código fornecido, que permite apresentar mensagens distintas para cada etapa do processo em curso.
if (isLoading) {
return (<h2>Loading...</h2>);
}
if (isError) {
return (<h2 className="error-message">{error.message}</h2>);
}
Em conclusão, é essencial fornecer o código para os componentes JavaScript que serão apresentados no navegador Web. Isto não só assegura a apresentação correcta dos componentes, como também permite a comunicação entre as aplicações do lado do cliente e do lado do servidor através da utilização de manipuladores de eventos e métodos de obtenção de dados.
Utilizando a funcionalidade oferecida pelo gancho useQuery, uma coleção de mensagens recuperadas é efetivamente acumulada dentro dos limites da variável de dados. Esta agregação serve para facilitar a manutenção do estado interno da aplicação. Posteriormente, pode ser efectuada uma análise da acumulação de mensagens armazenadas na referida variável, culminando na sua apresentação visual na interface de navegação.
Para dar aos utilizadores a possibilidade de navegar através de dados paginados adicionais, é necessário incorporar dois botões de navegação, denominados “Anterior” e “Seguinte”. Estes botões permitirão aos utilizadores aceder a mais páginas de conteúdo quando estas são apresentadas num formato paginado.
return (
<div>
<h2 className="header">Next.js Pagination</h2>
{data && (
<div className="card">
<ul className="post-list">
{data.map((post) => (
<li key={post.id} className="post-item">{post.title}</li>
))}
</ul>
</div>
)}
<div className='btn-container'>
<button
onClick={() => setPage(prevState => Math.max(prevState - 1, 0))}
disabled={page === 1}
className="prev-button"
>Prev Page</button>
<button
onClick={() => setPage(prevState => prevState \\+ 1)}
className="next-button"
>Next Page</button>
</div>
</div>
);
Por fim, inicie o servidor de desenvolvimento.
npm run dev
Por favor, navegue para " http://localhost:3000/Pagination " no seu browser para obter mais instruções.
A incorporação do componente Paginação na estrutura de directórios da aplicação faz com que o Next.js o reconheça como uma rota designada, permitindo uma navegação sem problemas para a página Web correspondente através do seu URL específico.
Rolagem infinita usando o gancho useInfiniteQuery
A rolagem infinita cria uma interface de usuário aparentemente ininterrupta, carregando dinamicamente conteúdo adicional à medida que o usuário continua a rolar. Um exemplo disso pode ser encontrado na funcionalidade do YouTube, em que novas entradas de vídeo são facilmente recuperadas e apresentadas sem qualquer interrupção observável à medida que se desce pela página web.
A utilização do gancho useInfiniteQuery
permite facilitar o scroll infinito através da recuperação de dados de um servidor remoto, em que as páginas subsequentes são automaticamente pedidas e apresentadas à medida que o utilizador desce progressivamente.
Para incorporar a funcionalidade de rolagem infinita em seu aplicativo, é necessário criar um novo arquivo JavaScript chamado “InfiniteScroll/page.js” e colocá-lo no diretório “src/app”. Posteriormente, será necessário importar vários módulos, incluindo componentes React e bibliotecas de gerenciamento de estado, que facilitam a integração perfeita desse recurso com sua base de código existente.
"use client"
import React, { useRef, useEffect, useState } from 'react';
import { useInfiniteQuery } from '@tanstack/react-query';
import './page.styles.css';
Em seguida, vamos desenvolver um componente funcional React e, dentro dele, estabelecer uma função que recupera o conteúdo dos posts de forma análoga ao método utilizado para paginação.
export default function InfiniteScroll() {
const listRef = useRef(null);
const [isLoadingMore, setIsLoadingMore] = useState(false);
const fetchPosts = async ({ pageParam = 1 }) => {
try {
const response = await fetch(`https://jsonplaceholder.typicode.com/posts?
_page=${pageParam}&_limit=5`);
if (!response.ok) {
throw new Error('Failed to fetch posts');
}
const data = await response.json();
await new Promise((resolve) => setTimeout(resolve, 2000));
return data;
} catch (error) {
console.error(error);
throw error;
}
};
// add the following code here
}
Ao contrário da implementação da paginação, que não introduz qualquer atraso na obtenção de dados, este código específico inclui uma pausa intencional de aproximadamente dois segundos antes de adquirir novas informações. Este atraso intencional foi concebido para dar aos utilizadores tempo suficiente para percorrerem e explorarem o conjunto de dados atual, resultando, em última análise, na recuperação automática de um conjunto de dados actualizados à medida que continuam a sua experiência de navegação.
Após a inicialização, o gancho personalizado useInfiniteQuery
estabelece uma ligação ao servidor, recuperando o conjunto inicial de dados aquando da apresentação do componente. Posteriormente, à medida que o utilizador percorre o conteúdo, o gancho obtém autonomamente os lotes de informação subsequentes e incorpora-os no componente sem necessidade de qualquer outra entrada.
const { data, fetchNextPage, hasNextPage, isFetching } = useInfiniteQuery({
queryKey: ['posts'],
queryFn: fetchPosts,
getNextPageParam: (lastPage, allPages) => {
if (lastPage.length < 5) {
return undefined;
}
return allPages.length \\+ 1;
},
});
const posts = data ? data.pages.flatMap((page) => page) : [];
A variável posts
funciona como um acumulador para recolher todos os posts de várias páginas e achata-os numa matriz unificada. Ao fazê-lo, simplifica o processo de iteração e renderização de cada mensagem individualmente.
É possível implementar uma funcionalidade que utiliza a API Intersection Observer para verificar quando elementos específicos entram nos limites do ecrã visível do utilizador, definindo uma função. Isto permite monitorizar o comportamento de deslocamento do utilizador e carregar automaticamente dados adicionais quando este se aproxima do fim de uma lista.
const handleIntersection = (entries) => {
if (entries[0].isIntersecting && hasNextPage && !isFetching && !isLoadingMore) {
setIsLoadingMore(true);
fetchNextPage();
}
};
useEffect(() => {
const observer = new IntersectionObserver(handleIntersection, { threshold: 0.1 });
if (listRef.current) {
observer.observe(listRef.current);
}
return () => {
if (listRef.current) {
observer.unobserve(listRef.current);
}
};
}, [listRef, handleIntersection]);
useEffect(() => {
if (!isFetching) {
setIsLoadingMore(false);
}
}, [isFetching]);
Em conclusão, incorpore os componentes JSX que serão apresentados no navegador Web.
return (
<div>
<h2 className="header">Infinite Scroll</h2>
<ul ref={listRef} className="post-list">
{posts.map((post) => (
<li key={post.id} className="post-item">
{post.title}
</li>
))}
</ul>
<div className="loading-indicator">
{isFetching ? 'Fetching...' : isLoadingMore ? 'Loading more...' : null}
</div>
</div>
);
Quando terminar as suas modificações, por favor, navegue até " http://localhost:3000/InfiniteScroll " para observar a sua funcionalidade em primeira mão.
TanStack Query: Mais do que apenas buscar dados
O TanStack Query mostra sua versatilidade como uma biblioteca de gerenciamento de dados abrangente por meio da implementação de recursos de paginação e rolagem infinita. Estas funcionalidades demonstram a vasta gama de capacidades oferecidas por esta poderosa ferramenta.
Através de um vasto conjunto de funcionalidades, esta solução optimiza a administração dos dados da aplicação, abrangendo um controlo eficaz do estado. Em conjunto com várias outras operações centradas em dados, melhora a eficiência operacional e a satisfação do utilizador de aplicações baseadas na Web.