Arreglando la paginación
Introducción al problema
La paginación funciona, pero hay un problema: la URL no refleja el estado real de la búsqueda ni de la página.
Mira lo que está pasando ahora:
- Buscas “React” en el buscador
- Cambias de página
- La URL no cambia o se queda siempre en
/search - Si recargas, pierdes la búsqueda
- Si copias y pegas la URL, la otra persona no ve lo mismo que tú
Y esto es un problema gordo, porque la URL debe ser la fuente de la verdad de tu aplicación.
Queremos que algo como:
/search?page=3&text=React
Signifique:
- Página actual: 3
- Filtro de texto: “React”
Y si recargas o compartes esa URL, se vea exactamente lo mismo.
En esta clase vamos a arreglar la generación de URLs en la paginación usando URLSearchParams. Todavía no vamos a sincronizar el estado con la URL (eso lo dejamos para la siguiente clase), pero sí vamos a conseguir que los enlaces de la paginación apunten a donde tienen que apuntar.
URL como fuente de la verdad
La idea base es simple pero importante:
Si en la URL pone que estás buscando React en la página 2, la aplicación debe leer eso como estado inicial y reflejarlo en la UI.
Las ventajas son claras:
- El usuario puede recargar sin miedo
- Puede guardar la URL en favoritos
- Puede compartirla con otra persona
- El botón Atrás del navegador tiene sentido
Arreglando los enlaces de la paginación
Problema actual
Ahora mismo los botones de paginación tienen algo así:
<a href="#">1</a>
<a href="#">2</a>
<a href="#">3</a>
O incluso todos apuntan al mismo sitio. Los enlaces están mal y todos van al hash.
Lo que necesitamos es que cada botón apunte a la URL correcta, conservando el resto de parámetros (por ejemplo el filtro de texto).
Creando buildPageURL con URLSearchParams
Dentro del componente de paginación, vamos a crear una función para construir la URL de cada página a partir de la URL actual:
// Pagination.jsx
const buildPageURL = (pageNumber) => {
// Tomamos la URL actual
const url = new URL(window.location.href)
// Trabajamos con search params
const searchParams = url.searchParams
// Actualizamos o creamos el parámetro "page"
searchParams.set('page', String(pageNumber))
// Importante: reasignamos los search params modificados
url.search = searchParams.toString()
// Devolvemos solo la parte "pathname + search"
return `${url.pathname}?${url.searchParams.toString()}`
}
Puntos clave de esto:
new URL(window.location.href)te da un objeto URL completourl.searchParamses una instancia deURLSearchParamssearchParams.set('page', '2')crea o actualiza el parámetro- No tiramos la URL actual, solo tocamos la página
Usando buildPageURL en la paginación
Aquí tienes un ejemplo de cómo quedaría el componente de paginación:
function Pagination({ currentPage, totalPages }) {
const buildPageURL = (pageNumber) => {
const url = new URL(window.location.href)
const searchParams = url.searchParams
searchParams.set('page', String(pageNumber))
url.search = searchParams.toString()
return `${url.pathname}?${url.searchParams.toString()}`
}
const previousPage = currentPage - 1
const nextPage = currentPage + 1
return (
<nav aria-label="Paginación">
<ul>
<li>
<a
href={buildPageURL(previousPage)}
aria-disabled={currentPage === 1}
onClick={(event) => {
if (currentPage === 1) event.preventDefault()
}}
>
Anterior
</a>
</li>
{Array.from({ length: totalPages }, (_, index) => {
const page = index + 1
return (
<li key={page}>
<a href={buildPageURL(page)} aria-current={page === currentPage ? 'page' : undefined}>
{page}
</a>
</li>
)
})}
<li>
<a
href={buildPageURL(nextPage)}
aria-disabled={currentPage === totalPages}
onClick={(event) => {
if (currentPage === totalPages) event.preventDefault()
}}
>
Siguiente
</a>
</li>
</ul>
</nav>
)
}
Aquí todavía no hay React Router, son links normales que:
- Navegan a
/ruta-actual?page=N&text=React - Mantienen otros parámetros de búsqueda
- Construyen bien cada enlace
Mejorando la experiencia con aria-disabled
Fíjate que estamos usando aria-disabled en lugar de deshabilitar el enlace completamente. Esto es importante para la accesibilidad:
<a
href={buildPageURL(previousPage)}
aria-disabled={currentPage === 1}
onClick={(event) => {
if (currentPage === 1) event.preventDefault()
}}
>
Anterior
</a>
Lo que hacemos es:
- Si estamos en la primera página,
aria-disabled={true} - El
onClickpreviene la navegación si estamos en la primera página - El enlace sigue siendo navegable por teclado, pero no hace nada
Lo mismo para el botón “Siguiente” cuando estamos en la última página.
Por qué esto es importante
Con estos cambios ya conseguimos que:
- Los enlaces sean correctos: Cada botón de página apunta a su URL correspondiente
- Se mantengan los filtros: Si estás buscando “React”, al cambiar de página sigues buscando “React”
- Sea compartible: Puedes copiar cualquier enlace de paginación y pegarlo en otra pestaña
- Funcione el botón derecho: Puedes hacer click derecho y “Abrir en nueva pestaña” en cualquier número de página
Resumen
En esta clase hemos visto:
- Por qué la URL debe ser la fuente de la verdad de tu aplicación
- Cómo usar
URLSearchParamspara construir URLs correctas - Cómo crear la función
buildPageURLpara generar enlaces de paginación - Cómo mantener los parámetros existentes al cambiar de página
- Cómo mejorar la accesibilidad con
aria-disabled
La paginación no es solo UI, también es:
- Estado compartible: puedes copiar y pegar la URL
- Estado reproducible: si recargas ves lo mismo
- Estado que se puede guardar: en favoritos, en enlaces, donde sea
En la siguiente clase vamos a sincronizar el estado de React con la URL, para que al cambiar de página o hacer una búsqueda, la URL se actualice automáticamente sin tener que recargar la página.