Paginación simple con NextJS y MUI
Cuando trabajamos desarrollando aplicaciones web una de las muchas necesidades a la que nos podemos enfrentar es a la de paginar el contenido.
Generalmente obtenemos un conjunto de datos, bien sea a través de la consulta a una API, una base de datos, o incluso datos almacenados directamente en nuestro frontend, en forma de array de objetos:
Un ejemplo podría ser:
const datos = [
{
id: 1,
nombre: Jennifer,
apellidos: Smith,
edad: 34
},
{
id: 2,
nombre: John,
apellidos: Doe,
edad: 40,
},
...
]
Una vez obtenidos los datos, es tarea de nuestro frontend el mostrarlos de una forma amigable en nuestra interfaz web y, para ello, debemos preocuparnos de evitar sobrecargar la pantalla en el caso de que sean muchos los datos obtenidos.
La solución más rápida y sencilla es paginar los resultados: mostraremos los datos agrupados en series de X datos y nos moveremos entre ellos a través de las "páginas".
Creando la estructura en NextJS
En mi proyecto Iteralia he implementado la paginación por componentes
import { Box } from '@mui/material';
import ListItem from './mods/ListItem';
import { useState, useEffect } from 'react';
import { DogRoute } from '../../interfaces/dogRoutes';
import { getRoutes } from '../../utils/routes';
import Pagination from './mods/Pagination';
const List = () => {
const [routeList, setRouteList] = useState<DogRoute[]>();
const [currentRouteList, setCurrentRouteList] = useState<DogRoute[]>();
const [currentPage, setCurrentPage] = useState<number>(0);
const [totalPages, setTotalPages] = useState<number>(0);
useEffect(() => {
const fetchData = async () => {
const routes = await getRoutes();
if (routes) {
setRouteList(routes);
setCurrentRouteList(routes.slice(currentPage, 6))
setTotalPages(routes.length);
}
}
fetchData();
}, [])
useEffect(() => {
if (routeList) {
setCurrentRouteList(routeList.slice(currentPage, currentPage + 6))
}
}, [currentPage])
return(
<Box>
<Box>
{currentRouteList && currentRouteList.map(route => <ListItem route={route} />)}
</Box>
<Box>
<Pagination total={totalPages} setCurrentPage={setCurrentPage} />
</Box>
</Box>
);
}
En este primer componente voy a mostrar los items extraídos de la API (gracias a la función getRoutes()
y mediante el uso de useEffect) pero sólo los primeros 6 haciendo uso del método Array.slice()
.
Sé que es una forma poco eficiente puesto que la petición implica obtener todos los resultados de la API, pero en estas primeras versiones del proyecto el número de resultados es tremendamente reducido y considero que es más cómodo realizar el proceso en el lado del cliente, una vez obtenidos todos los datos, y jugando con lo que queremos que se muestre.
La forma de controlar en qué página estoy y, por ende, de dónde a dónde "corto" los resultados del Array lo gestiona el componente Pagination:
import { Pagination } from "@mui/material";
import { FC } from "react";
interface Props {
total: number;
setCurrentPage: (currentPage: number) => void;
}
const PaginationAdmin: FC<Props> = ({ total, setCurrentPage }) => {
const handleChange = (event: React.ChangeEvent<unknown>, value: number) => {
setCurrentPage((value - 1) * 6);
};
const pages = Math.ceil(total / 6);
return (
<Pagination count={pages} color="primary" onChange={handleChange} />
)
}
export default PaginationAdmin
Como véis, es en este componente donde se implementa el Pagination
de MUI, (no confundir con mi propio componente Pagination). Este componente muestra el número de páginas que le pasemos en count y maneja los cambios en su onChange
.
<Pagination count={pages} color="primary" onChange={handleChange} />
El valor obtenido es el que vamos a pasar al estado de la página mediante setCurrentPage()
para que el componente List muestre adecuadamente los items basándonos en el número de página actual.
De esta forma se nos muestra un paginador muy sencillo y funcional:
Lo que nos permite movernos entre ellos e interactuar de forma mucho más cómoda y estética.