¡En este artículo, exploraremos cómo crear una página de feed usando React Query!
Esto es lo que crearemos:
Este artículo no cubrirá todos los pasos y detalles involucrados en la creación de la aplicación.
En cambio, nos centraremos en las características clave, específicamente las funcionalidades de "desplazamiento infinito" y "desplazamiento hacia arriba".
Si está interesado en consultar la implementación completa, puede encontrar el código base completo en este repositorio de GitHub.
Primero, crearemos nuestra aplicación React usando Vite con el siguiente comando:
npm create vite@latest feed-page-rq -- --template react-ts
Y instalaremos las dependencias requeridas, axios y react-query:
npm install axios @tanstack/react-query@4
También necesitamos simular un servidor RESTful, por lo que usaremos json-server, que nos permite simular un backend proporcionando puntos finales API falsos para nuestra aplicación React.
Trabajaremos con una entidad de publicación que incluye los siguientes atributos:
{ "id": "1", "title": "Sample Post Title", "body": "This is a sample post body", "userId": "2", "createdAt": 1728334799169 // date timestamp }
Una vez configurado el servidor, lo ejecutaremos usando:
npx json-server --watch db.json
El mecanismo de la función "Desplazamiento infinito" es sencillo:
Cuando el usuario se desplaza por la lista de publicaciones y se acerca al fondo del contenedor, React Query buscará el siguiente lote de publicaciones. Este proceso se repite hasta que no haya más publicaciones para cargar.
Verificamos si el usuario está cerca del final agregando la posición de desplazamiento actual (scrollTop) a la altura visible de la pantalla (clientHeight) y comparando esta suma con el altura total del contenedor (scrollHeight).
Si la suma es mayor o igual a la altura total del contenedor, le pedimos a React Query que busque la página siguiente.
const { scrollTop, scrollHeight, clientHeight } = elemRef.current; if(scrollTop clientHeight >= scrollHeight) { fetchNextPage(); }
Primero, crearemos un gancho personalizado para ajustar useInfiniteQuery de React Query.
Dentro del gancho personalizado, configuramos la consulta para recuperar publicaciones página por página, especificando el número de página inicial y la función que recupera las siguientes 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, }); };
Ahora, usaremos el enlace personalizado en nuestro componente para mostrar la lista de publicaciones.
Para hacer esto, primero recorremos las páginas y luego iteramos sobre las publicaciones dentro de cada página para representarlas.
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 el comportamiento de desplazamiento infinito, necesitamos agregar un detector de eventos de desplazamiento al contenedor donde se muestran las publicaciones. Este detector de eventos activa la función onScroll, que verifica si el usuario está cerca del fondo del contenedor y, de ser así, llama a fetchNextPage para cargar más contenido.
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 continuación, crearemos un botón "Desplazarse hacia arriba" que aparece cuando se agrega una nueva publicación. Este botón permite al usuario regresar rápidamente a la parte superior para ver la última actualización.
Dado que las publicaciones están ordenadas por fecha de creación, cualquier publicación recién agregada aparecerá en la parte superior de la lista.
La lógica de nuestra función se construirá sobre esta premisa.
Comenzamos creando una nueva consulta para buscar y almacenar en caché la última publicación creada. Llamaremos a esta publicación prevNewestPost.
Queremos que prevNewestPost se quede unos pasos por detrás, o como máximo, coincida con la primera publicación de la lista. Entonces, controlaremos manualmente su recuperación.
Esto lo lograremos configurando enable: false en las opciones 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, }); };
Con React Query, la lista de publicaciones se actualiza automáticamente en eventos específicos. (Aquí está el enlace de documentación para obtener una lista completa de estos eventos).
Usaremos esta lista actualizada para determinar cuándo mostrar el botón 'Desplazarse hacia arriba' comparando prevNewestPost con la primera publicación.
Si son diferentes, esto indica que se ha agregado una nueva publicación, por lo que se mostrará el botón 'Desplazarse hacia arriba'.
setIsShowButton(postLists?.pages[0].data[0].id !== prevNewestPost?.id);
No deberíamos mostrar el botón "Desplazarse hacia arriba" cuando el usuario se encuentra en la parte superior del contenedor de lista de publicaciones.
Entonces, cuando el usuario llega a la cima, debemos resincronizar el prevNewestPost con la última publicación actual activando una nueva búsqueda 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]);
Al hacer clic en el botón ToTopBtn, se desplazará hasta la parte superior de la lista, lo que activará la lógica existente para ocultar el botón y volver a recuperar datos para sincronizar prevNewestPost con la primera publicación de la lista.
import { RefObject } from "react"; type ToTopBtnProps = { elemRef: RefObject; }; export default function ToTopBtn({ elemRef }: ToTopBtnProps) { return ( ); }
Para probar la funcionalidad del botón "Desplazarse hacia arriba", debemos agregar nuevas publicaciones al feed.
Para esto, usaremos useMutation de React Query para agregar una nueva publicación al servidor y revalidar nuestra lista de publicaciones almacenada en caché después de cada mutación.
Configuraremos una mutación que nos permitirá crear una nueva publicación con datos aleatorios cada vez que el usuario haga clic en un botón.
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 ();
En este tutorial, exploramos el poder de React Query a través de un caso de uso real, destacando su capacidad para ayudar a los desarrolladores a crear interfaces dinámicas que mejoren la experiencia del usuario.
Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.
Copyright© 2022 湘ICP备2022001581号-3