El infinite scroll (o desplazamiento infinito) es una técnica que carga más contenido a medida que el usuario se desplaza hacia abajo en una página web, ofreciendo una experiencia fluida y sin interrupciones. En lugar de hacer clic en un botón para cargar más contenido, el infinite scroll carga automáticamente el siguiente lote de datos cuando el usuario llega al final de la página. Esta técnica es popular en aplicaciones que manejan grandes cantidades de datos, como redes sociales, tiendas en línea o portales de noticias.
La API de Intersection Observer nos permite observar un elemento del DOM y detectar cuándo entra o sale del viewport. Esto es extremadamente útil para casos como el lazy loading de imágenes, el seguimiento de la visibilidad de anuncios o, en nuestro caso, cargar más elementos en una lista cuando el usuario se acerca al final.
Vamos a crear un ejemplo sencillo de infinite scroll en el que cargaremos una lista de elementos en lotes. Cada vez que el usuario llegue al final de la lista, se cargará un nuevo lote de datos.
Si aún no tienes un proyecto de React, puedes crearlo con:
npx create-react-app infinite-scroll
cd infinite-scroll
Una vez creado el proyecto, borra los archivos que no vayas a usar (como logo.svg
y otros archivos innecesarios) y asegúrate de que el archivo App.js
esté limpio para empezar a trabajar.
Vamos a crear un componente de lista que cargará más elementos a medida que el usuario se desplace hacia abajo.
1. Estado: Necesitaremos manejar dos estados, uno para la lista de elementos y otro para determinar si estamos cargando más datos.
2. useEffect: Este hook se usará para manejar el efecto secundario de observar el elemento.
3. Intersection Observer: Utilizaremos esta API para observar el último elemento de la lista y cargar más datos cuando sea visible.
import React, { useState, useEffect, useRef } from "react";
const InfiniteScroll = () => {
const [items, setItems] = useState(Array.from({ length: 20 }));
const [isFetching, setIsFetching] = useState(false);
const observerRef = useRef(null);
// Simulación de una función para cargar más datos
const fetchMoreData = () => {
setTimeout(() => {
setItems((prevItems) => [
...prevItems,
...Array.from({ length: 20 }), // Agregamos 20 nuevos elementos
]);
setIsFetching(false);
}, 1500);
};
// Intersection Observer
useEffect(() => {
if (isFetching) return;
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
setIsFetching(true);
}
});
if (observerRef.current) {
observer.observe(observerRef.current);
}
return () => {
if (observerRef.current) {
observer.unobserve(observerRef.current);
}
};
}, [isFetching]);
useEffect(() => {
if (!isFetching) return;
fetchMoreData();
}, [isFetching]);
return (
<div>
<h1>Infinite Scroll con Intersection Observer</h1>
<ul>
{items.map((_, index) => (
<li key={index} style={{ padding: "20px", border: "1px solid black" }}>
Elemento {index + 1}
</li>
))}
</ul>
<div ref={observerRef} style={{ height: "20px" }} />
{isFetching && <p>Cargando más elementos...</p>}
</div>
);
};
export default InfiniteScroll;
1. Estado items
: Este estado contiene la lista de elementos que se van a renderizar. Inicialmente, llenamos esta lista con 20 elementos ficticios usando Array.from({ length: 20 })
.
2. Estado isFetching
: Este estado es un flag que indica si estamos en el proceso de cargar más datos.
3. observerRef
: Usamos un ref
para referenciar el último elemento en la lista, el cual será observado por la API de Intersection Observer.
4. IntersectionObserver
:
IntersectionObserver
dentro del hook useEffect
.
observerRef
entra en el viewport. Si es así, activamos el estado isFetching
para indicar que debemos cargar más datos.
isFetching
es true
, llamamos a fetchMoreData
que simula la carga de nuevos datos agregando 20 más elementos a la lista.
Para evitar que el infinite scroll siga cargando datos indefinidamente, podrías agregar una condición de límite de elementos:
const [hasMore, setHasMore] = useState(true);
const fetchMoreData = () => {
if (items.length >= 100) {
setHasMore(false);
return;
}
// Lógica de carga de más datos
};
De esta manera, solo cargaremos más elementos mientras el total sea menor que un valor específico, como 100 en este caso.
Puedes agregar un spinner o una animación mientras se cargan nuevos elementos, en lugar del simple texto "Cargando más elementos...".
{isFetching && hasMore && <div className="spinner">Cargando...</div>}
Agrega algunos estilos básicos para que la lista se vea mejor:
ul {
list-style: none;
padding: 0;
}
li {
margin-bottom: 10px;
background: #f0f0f0;
padding: 20px;
text-align: center;
}
.spinner {
text-align: center;
padding: 10px;
}
El uso de Intersection Observer para implementar un infinite scroll en React es una solución eficiente y moderna para cargar más contenido a medida que el usuario se desplaza. La API nativa se encarga de observar el último elemento visible en la lista, evitando la necesidad de calcular manualmente cuándo estamos al final del scroll, lo cual mejora el rendimiento de nuestra aplicación.
Este enfoque es ideal para aplicaciones que manejan grandes cantidades de datos o listas dinámicas. Recuerda que siempre puedes personalizar y optimizar la lógica según tus necesidades, controlando la cantidad de datos cargados, el tiempo de espera, o añadiendo otras características como filtros o paginación dinámica.
¿Listo para poner en marcha un scroll infinito en tu aplicación? ¡Espero que esta guía te haya sido de ayuda! 😃😃
Jorge García
Fullstack developer