Saltearse al contenido

Navegación

La navegación en una aplicación web es un aspecto fundamental para la experiencia de usuario. En una aplicación de una sola página (SPA), la navegación se realiza sin necesidad de recargar la página, lo que permite una experiencia de usuario más fluida y rápida.

De momento, hemos visto cómo definir rutas en una aplicación React utilizando React Router. Sin embargo, no hemos considerado cómo navegar entre estas rutas, ni cómo pasar información entre ellas.

Enlaces

Para navegar entre rutas en HTML, se utilizan los elementos <a> con el atributo href, en el cual se especifica la URL de destino. Sin embargo, en una aplicación que implemente React Router, se debe utilizar el componente Link para navegar entre rutas.

Este componente se encarga de actualizar la URL del navegador de forma programática accediendo a la API de Historial de navegación de HTML5 que administra el enrutador configurado, BrowserRouter.

1
import { Link } from 'react-router-dom';
2
3
function Navbar() {
4
return (
5
<nav>
6
<ul>
7
<li><Link to="/">Home</Link></li>
8
<li><Link to="/products">Products</Link></li>
9
<li><Link to="/contact">Contact</Link></li>
10
</ul>
11
</nav>
12
);
13
}

Como se puede observar, el componente Link recibe una prop to que especifica la ruta a la que se desea navegar. Al hacer clic en el enlace, el componente Link se encargará de actualizar la URL del navegador y renderizar el componente asociado a la ruta especificada (si existe).

En definitiva, el componente Link renderiza un elemento <a> con el atributo href que apunta a la ruta especificada en la prop to, pero nos brinda un comportamiento optimizado mediante el uso del enrutador configurado.

Configuración de rutas relativas

En un componente Link, se puede establecer una ruta relativa o absoluta.

  • Una ruta absoluta (que comienza con /) se resuelve en relación con la raíz del enrutador, es decir, la URL base de la aplicación.

  • Una ruta relativa (que no comienza con /) se resuelve en relación con la ruta padre, lo que significa que se basa en la ruta URL que coincidió con la ruta que renderizó ese Link. Puede contener .. para enlazar a rutas más arriba en la jerarquía. En estos casos, .. funciona exactamente como la función cd de la línea de comandos; cada .. elimina un segmento de la ruta padre.

    Por ejemplo, si se tiene la siguiente estructura de rutas:

    1
    <Route path="/products" component={Products} />
    2
    <Route path="/products/:id" component={ProductDetail} />

    Y se desea navegar a la ruta /products desde la ruta /products/:id, se puede utilizar un enlace relativo de la siguiente manera:

    1
    <Link to="../products">Back to products</Link>

    Alternativamente, se puede utilizar la prop relative para especificar que la ruta es relativa. En este caso, se puede utilizar un componente Link con la prop relative="path" para indicar que la ruta es relativa a la ruta actual, con lo cual sólo se necesita indicar ".." para retroceder un nivel en la jerarquía de rutas.

    1
    <Link to=".." relative="path">Back to products</Link>

Propiedad replace

Por otro lado, la prop replace permite reemplazar la entrada actual en el historial de navegación en lugar de agregar una nueva entrada. Esto puede ser útil en casos en los que no se desea que el usuario pueda regresar a la página anterior.

1
<Link to="/products" replace>Products</Link>

De esta manera, si el stack de navegación es /home -> /products -> /products/:id -> /contact, entonces nos encontramos en la ruta /contact y, al navegar mediante el componente Link configurado de la siguiente manera:

1
<Link to="/about" replace>
2
About
3
</Link>

El stack de navegación se convertirá en /home -> /products -> /products/:id -> /about. Es decir, la ruta /contact se reemplazará por la ruta /about.

Propiedad state

Por último, la prop state permite pasar información adicional a la ruta de destino, la cual se puede acceder desde la ubicación de la ruta en el componente asociado.

1
<Link
2
to="/login"
3
state={{ from: "home" }}
4
replace
5
>
6
Products
7
</Link>

En este caso, al navegar a la ruta /login, se pasará un objeto con la propiedad from y el valor 'home' como información adicional. Esta información se puede acceder desde la ubicación de la ruta en el componente asociado.

Enlaces activos

Cabe mencionar que existen otros componentes que representan enlaces en React Router, como NavLink.

Un NavLink es un tipo especial de Link que sabe si está “activo”, “pendiente” o “en transición”. Esto es útil en varios escenarios:

  • Al construir un menú de navegación, como un breadcrumb o un conjunto de pestañas donde se desea mostrar cuál de ellos está seleccionado actualmente.
  • Proporciona un contexto útil para tecnologías de asistencia como lectores de pantalla.
  • Proporciona un valor “en transición” para brindar un control más detallado sobre las transiciones de vista.
1
import { NavLink } from 'react-router-dom';
2
3
<NavLink
4
to="/messages"
5
className={({ isActive, isPending, isTransitioning }) =>
6
[
7
isPending ? "pending" : "",
8
isActive ? "active" : "",
9
isTransitioning ? "transitioning" : "",
10
].join(" ")
11
}
12
>
13
Products
14
</NavLink>

Si bien NavLink establece ciertos estilos por defecto para los enlaces activos y estos son claramente visibles, se puede personalizar el estilo de los enlaces activos mediante la prop className. Podemos establecer condicionalmente clases CSS basadas en las propiedades isActive, isPending e isTransitioning, tal como se muestra en el ejemplo anterior.

Redirecciones

En una aplicación web, es común que se deba redirigir al usuario a una ruta diferente. Para ello, se puede utilizar la función redirect, la cual requiere de un parámetro que especifica la URL de destino.

1
import { redirect } from 'react-router-dom';
2
3
function Home() {
4
return (
5
<button onClick={() => redirect('/products')}>
6
Go to products
7
</button>
8
);
9
}

Si bien la función redirect es útil en muchos casos, no es la única forma de redirigir a los usuarios. Sse puede utilizar un hook llamado useNavigate, que devuelve una función que permite navegar programáticamente a una ruta diferente.

1
import { useNavigate } from 'react-router-dom';
2
3
function Home() {
4
const navigate = useNavigate();
5
6
return (
7
<button onClick={() => navigate('/products')}>
8
Go to products
9
</button>
10
);
11
}

La función devuelta por useNavigate puede configurarse con parámetros adicionales para tener un comportamiento similar al del componente Link, como relative, replace, state, entre otros. Por esta razón, useNavigate es una alternativa más flexible y poderosa que redirect.

El uso de useNavigate es generalmente más ventajoso que redirect, pero si no se requiere una configuración adicional, redirect es la opción más adecuada.

Estado de una ruta

Cada ruta en React Router tiene asociada cierta información que se puede acceder desde el componente asociado a la ruta. Esta información incluye la ubicación actual, los parámetros de la ruta, el historial de navegación, entre otros.

Location

El término “location” en React Router se refiere a la interfaz Location de la librería history, la cual es una dependencia de React Router que proporciona una API para interactuar con el historial de navegación del navegador. La interfaz Location representa la ubicación actual en la aplicación, incluyendo la URL actual, el historial de navegación, entre otros.

Esta es fundamental para la navegación, ya que es la que permite al enrutador BrowserRouter saber qué componente debe renderizar en función de la URL actual. Para esto, se vale de un objeto location que contiene toda esta información.

Por supuesto, podemos interactuar con este objeto accediendo a sus propiedades mediante el hook useLocation.

1
import { useLocation } from 'react-router-dom';
2
3
function Home() {
4
const location = useLocation();
5
6
return (
7
...
8
);
9
}

Este cuenta con las siguientes propiedades:

  • pathname: la ruta de la URL actual. Por ejemplo, si la URL actual es http://localhost:3000/products, el valor de pathname será /products.
  • search: la cadena de consulta de la URL actual. Por ejemplo, si la URL actual es http://localhost:3000/products?category=electronics, el valor de search será ?category=electronics. Esta propiedad es accesible también mediante el hook useParams.
  • hash: el fragmento de la URL actual. Por ejemplo, si la URL actual es http://localhost:3000/products#top, el valor de hash será #top. Este fragmento se utiliza comúnmente para enlazar a una sección específica dentro de un mismo componente.
  • state: La información adicional que se pasó a la ruta de destino mediante la prop state de un componente Link o mediante el hook useNavigate.
  • key: cada ruta cuenta con una clave única que se utiliza para identificarla en el historial de navegación.

Parámetros de ruta

Como vimos anteriormente, las rutas pueden contener parámetros que se utilizan para identificar recursos específicos. Por ejemplo, en la ruta /products/:id, :id es un parámetro que se utiliza para identificar un producto específico.

Estos parámetros se pueden acceder desde el componente asociado a la ruta mediante el hook useParams.

1
import { useParams } from 'react-router-dom';
2
3
function ProductDetail() {
4
const { id } = useParams();
5
6
return (
7
<div>
8
<h1>Product detail</h1>
9
<p>Product ID: {id}</p>
10
</div>
11
);
12
}

El uso de useParams nos permite evitar un acoplamiento directo entre la URL y el componente, como el que se presenta al acceder a window.location.pathname o mediante prop drilling.

Bibliografía