Neste artigo, exploraremos como construir uma página de feed usando React Query!
Aqui está o que criaremos:
Este artigo não cobrirá todas as etapas e detalhes envolvidos na construção do aplicativo.
Em vez disso, nos concentraremos nos principais recursos, especificamente nas funcionalidades de “rolagem infinita” e “rolagem para cima”.
Se você estiver interessado em consultar toda a implementação, você pode encontrar a base de código completa neste repositório GitHub.
Primeiro, criaremos nossa aplicação React usando Vite com o seguinte comando:
npm create vite@latest feed-page-rq -- --template react-ts
E instalaremos as dependências necessárias, axios e react-query:
npm install axios @tanstack/react-query@4
Também precisamos simular um servidor RESTful, então usaremos json-server, que nos permite simular um back-end fornecendo endpoints de API falsos para nosso aplicativo React.
Trabalharemos com uma entidade de postagem que inclui os seguintes atributos:
{ "id": "1", "title": "Sample Post Title", "body": "This is a sample post body", "userId": "2", "createdAt": 1728334799169 // date timestamp }
Depois que o servidor estiver configurado, iremos executá-lo usando:
npx json-server --watch db.json
O mecanismo do recurso "Rolagem Infinita" é direto:
Quando o usuário percorre a lista de postagens e se aproxima da parte inferior do contêiner, o React Query procurará o próximo lote de postagens. Este processo se repete até que não haja mais postagens para carregar.
Verificamos se o usuário está próximo ao final adicionando a posição atual de rolagem (scrollTop) à altura visível da tela (clientHeight) e comparando essa soma com o altura total do contêiner (scrollHeight).
Se a soma for maior ou igual à altura total do contêiner, pedimos ao React Query para buscar a próxima página.
const { scrollTop, scrollHeight, clientHeight } = elemRef.current; if(scrollTop clientHeight >= scrollHeight) { fetchNextPage(); }
Primeiro, criaremos um gancho personalizado para envolver useInfiniteQuery do React Query.
Dentro do hook customizado, configuramos a consulta para buscar posts página por página, especificando o número da página inicial e a função que recupera as próximas páginas:
import { QueryFunctionContext, useInfiniteQuery } from "@tanstack/react-query"; import axios from "axios"; const URL = "http://localhost:3000"; const POSTS = "posts"; export const fetchInfinitePosts = async ({ pageParam, }: QueryFunctionContext) => { const result = await axios.get( `${URL}/${POSTS}?_sort=-createdAt&_page=${pageParam}&_per_page=10`, ); return result.data; }; export const useInfinitePosts = () => { return useInfiniteQuery({ queryKey: [POSTS], queryFn: fetchInfinitePosts, initialPageParam: 1, getNextPageParam: (lastPage) => lastPage.next, }); };
Agora, usaremos o gancho personalizado em nosso componente para exibir a lista de postagens.
Para fazer isso, primeiro percorremos as páginas e, em seguida, iteramos as postagens em cada página para renderizá-las.
import { useInfinitePosts } from './hooks/useInfinitePosts'; const PostList = () => { const { data: postLists } = useInfinitePosts(); return ({postLists?.pages.map((page) => page.data.map(post => (); }; export default PostList;)) )}{post.title}
{post.body}
Para implementar o comportamento de rolagem infinita, precisamos adicionar um ouvinte de evento de rolagem ao contêiner onde as postagens são exibidas. Este ouvinte de evento aciona a função onScroll, que verifica se o usuário está próximo ao final do contêiner e, em caso afirmativo, chama fetchNextPage para carregar mais conteúdo.
import React, { useRef, useEffect } from 'react'; import { useInfinitePosts } from './hooks/useInfinitePosts'; const PostList = () => { const { data: postLists, fetchNextPage } = useInfinitePosts(); const elemRef = useRef(null); const onScroll = useCallback(() => { if (elemRef.current) { const { scrollTop, scrollHeight, clientHeight } = elemRef.current; const isNearBottom = scrollTop clientHeight >= scrollHeight; if(isNearBottom) { fetchNextPage(); } } }, [fetchNextPage]); useEffect(() => { const innerElement = elemRef.current; if (innerElement) { innerElement.addEventListener("scroll", onScroll); return () => { innerElement.removeEventListener("scroll", onScroll); }; } }, [onScroll]); return ({postLists?.pages.map((page, i) => page.data.map(post => (); }; export default PostList;)) )}{post.title}
{post.body}
A seguir, criaremos um botão "Rolar para o topo" que aparece quando uma nova postagem é adicionada. Este botão permite que o usuário retorne rapidamente ao topo para ver a atualização mais recente.
Como as postagens são classificadas por data de criação, qualquer postagem recém-adicionada aparecerá no topo da lista.
A lógica do nosso recurso será construída sobre essa premissa.
Começamos criando uma nova consulta para buscar e armazenar em cache a última postagem criada. Chamaremos esta postagem de prevNewestPost.
Queremos que prevNewestPost fique alguns passos atrás ou, no máximo, corresponda ao primeiro post da lista. Portanto, controlaremos manualmente sua nova busca.
Conseguiremos isso definindo enabled: false nas opções de consulta.
export const fetchNewestPost = async () => { const result = await axios.get(`${URL}/${POSTS}?_sort=-createdAt`); return result.data[0]; }; export const useNewestPost = () => { return useQuery({ queryKey: [POSTS, "newest"], queryFn: () => fetchNewestPost(), enabled: false, }); };
Com React Query, a lista de postagens é atualizada automaticamente em eventos específicos. (Aqui está o link da documentação para uma lista completa desses eventos.)
Usaremos esta lista atualizada para determinar quando exibir o botão 'Rolar para o topo' comparando prevNewestPost com a primeira postagem.
Se forem diferentes, isso indica que uma nova postagem foi adicionada, então o botão 'Rolar para o topo' será mostrado.
setIsShowButton(postLists?.pages[0].data[0].id !== prevNewestPost?.id);
Não devemos mostrar o botão "Rolar para o topo" quando o usuário estiver no topo do contêiner da lista de postagens.
Portanto, quando o usuário chega ao topo, precisamos sincronizar novamente o prevNewestPost com a última postagem atual, acionando uma nova busca de consulta.
const { data: prevNewestPost, refetch } = useNewestPost(); const [isShowButton, setIsShowButton] = useState(false); useEffect(() => { if (!isNearTop) { setIsShowButton(postLists?.pages[0].data[0].id !== prevNewestPost?.id); } else { setIsShowButton(false); refetch(); } }, [postLists, prevNewestPost, isNearTop]);
Clicar no botão ToTopBtn rolará para o topo da lista, acionando a lógica existente para ocultar o botão e buscar novamente os dados para sincronizar prevNewestPost com a primeira postagem da lista.
import { RefObject } from "react"; type ToTopBtnProps = { elemRef: RefObject; }; export default function ToTopBtn({ elemRef }: ToTopBtnProps) { return ( ); }
Para testar a funcionalidade do botão "Rolar para o topo", precisamos adicionar novas postagens ao feed.
Para isso, usaremos useMutation do React Query para adicionar uma nova postagem ao servidor e revalidar nossa postList em cache após cada mutação.
Configuraremos uma mutação que nos permitirá criar uma nova postagem com dados aleatórios sempre que o usuário clicar em um botão.
export const savePost = async (post: NewPostData) => axios.post(`${URL}/${POSTS}`, post); export const useAddPost = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: savePost, onSuccess: () => { queryClient.invalidateQueries({ queryKey: [POSTS] }); }, }); };
export default function AddNewPostBtn() { const mutation = useAddPost(); return ();
Neste tutorial, exploramos o poder do React Query por meio de um caso de uso real, destacando sua capacidade de ajudar os desenvolvedores a construir interfaces dinâmicas que melhoram a experiência do usuário.
Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.
Copyright© 2022 湘ICP备2022001581号-3