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 completo
  • url.searchParams es una instancia de URLSearchParams
  • searchParams.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 onClick previene 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:

  1. Los enlaces sean correctos: Cada botón de página apunta a su URL correspondiente
  2. Se mantengan los filtros: Si estás buscando “React”, al cambiar de página sigues buscando “React”
  3. Sea compartible: Puedes copiar cualquier enlace de paginación y pegarlo en otra pestaña
  4. 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 URLSearchParams para construir URLs correctas
  • Cómo crear la función buildPageURL para 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.