Les comparto mi solución para el reto de paginación
Es un componente que lo llamé Paginate y usa la UI de Tailwind
Para la lógica le paso estas props:
totalItems: El total de todos los elementos (llamando a la API con el offset=0 y limit=0 y luego aplicando un .length)
itemsPerPage: Los elementos a mostrar por página, sería el PRODUCT_LIMIT
setOffset: Le paso la función que actualiza el estado offsetProducts para realizar la llamada a la API (sería el resultado de la página donde estoy, menos 1 y multiplicado por los elementos que se muestran)
neighbours: La cantidad de números que se van a mostrar a los lados de la página seleccionada. Siempre se van a mostrar la misma cantidad, o menos si el total de páginas es inforior
Hago los cálculos para generar un lista con las páginas.
Utilizo una variable de estado current que se actualiza al seleccionar un página o al siguiente o anterior y ahí también es donde utilizo setOffset(), que en realidad es setOffsetProducts. Al actualizar la variable de estado offsetProducts que la paso como variable al endpoint se actualiza la url de la API, por lo que aprovecho el useEffect de useFecth pasándole como parámetro el endpoint para que llame a la API
import React, { useState } from 'react';
import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/solid';
const Paginate = ({ totalItems, itemsPerPage, neighbours, setOffset }) => {
const items = [];
const [current, setCurrent] = useState(1);
const totalPage = Math.ceil(totalItems / itemsPerPage);
const end = Math.min(Math.max(neighbours * 2 + 2, neighbours + current + 1), totalPage + 1);
const start = Math.min(Math.max(end - (neighbours * 2 + 1), 1), Math.max(current - neighbours, 1));
for (let i = start; i < end; i++) {
items.push(
<a
key={`Paginador-${i}`}
onClick={() => {
setCurrent(i);
setOffset((i - 1) * itemsPerPage);
}}
href="#"
aria-current="page"
className={`${getClassActive(i)} relative inline-flex items-center px-4 py-2 border text-sm font-medium`}
>
{i}
</a>
);
}
function getClassActive(i) {
return i === current ? 'z-10 bg-indigo-50 border-indigo-500 text-indigo-600' : 'bg-white border-gray-300 text-gray-500 hover:bg-gray-50';
}
function prevPage() {
if (current > 1) {
setCurrent(current - 1);
setOffset((current - 2) * itemsPerPage);
}
}
function nextPage() {
if (current < totalPage) {
setCurrent(current + 1);
setOffset(current * itemsPerPage);
}
}
return (
<div className="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6">
<div className="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
<div>
<p className="text-sm text-gray-700">
Showing <span className="font-medium">{itemsPerPage * (current - 1) + 1}</span> to{' '}
<span className="font-medium">{current * itemsPerPage < totalItems ? current * itemsPerPage : totalItems}</span> of <span className="font-medium">{totalItems}</span> results
</p>
</div>
<div>
<nav className="relative z-0 inline-flex rounded-md shadow-sm -space-x-px" aria-label="Pagination">
<a
onClick={() => prevPage()}
href="#"
className="bg-white border-gray-300 text-gray-500 hover:bg-gray-50 relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"
>
<span className="sr-only">Previous</span>
<ChevronLeftIcon className="h-5 w-5" aria-hidden="true" />
</a>
{items}
<a
onClick={() => nextPage()}
href="#"
className="relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"
>
<span className="sr-only">Next</span>
<ChevronRightIcon className="h-5 w-5" aria-hidden="true" />
</a>
</nav>
</div>
</div>
</div>
);
};
export default Paginate;
En el Dashboard la variable de estado y las llamadas
const [offsetProducts, setOffsetProducts] = useState(0);
const products = useFecth(endPoints.products.getProducts(PRODUCT_LIMIT, offsetProducts), offsetProducts);
const totalProducts = useFecth(endPoints.products.getProducts(0, 0)).length;
Para mostrar el paginador
{totalProducts > 0 && <Paginate totalItems={totalProducts} itemsPerPage={PRODUCT_LIMIT} setOffset={setOffsetProducts} neighbours={3}></Paginate>}
En el useEffect de useFetch
useEffect(() => {
try {
fecthData();
} catch (error) {
console.log(error);
}
}, [endPoint]);
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?
o inicia sesión.