Saltearse al contenido

Introducción al enrutamiento

Antes de analizar las estrategias recomendadas de enrutamiento en una aplicación React, es importante tener claro qué es el enrutamiento y por qué es necesario.

Como sabemos, una red de computadoras esta conformada por un conjunto de dispositivos, llamados nodos, interconectados entre sí y que se comunican a través de enlaces o caminos, a los que típicamente denominamos como rutas. Entonces, el enrutamiento es el proceso de selección de la mejor ruta para que dos nodos puedan comunicarse, siguiendo ciertas reglas predefinidas.

En el contexto de una aplicación web, el enrutamiento se refiere a la selección de la vista que se debe mostrar al usuario, en función de la URL que este ha solicitado. Por ejemplo, si el usuario solicita la URL https://www.midominio.com/productos, la aplicación debe mostrar la vista correspondiente a la lista de productos.

Por lo tanto, el enrutamiento es una parte fundamental de cualquier aplicación web moderna, ya que, en la mayoría de los casos, las aplicaciones web son aplicaciones de una sola página (SPA). Es decir, aplicaciones que cargan una única página HTML y luego actualizan dinámicamente el contenido de la página, sin necesidad de recargarla completamente (del mismo modo que lo hace una aplicación de escritorio y, como vimos anteriormente, una aplicación React).

En una SPA, el enrutamiento es necesario para que la aplicación pueda cambiar la vista que se muestra al usuario.

Enrutadores

Un enrutador, o mejor conocido por su término en inglés router, es un componente que se encarga de gestionar las rutas de una aplicación web.

Tipos de enrutamiento

Existen dos tipos de enrutamiento, los cuales se pueden clasificar de acuerdo a la forma en que se determina la ruta que deben seguir los datos:

Enrutamiento estático

En este tipo de enrutamiento, un administrador de red emplea una tabla de enrutamiento para configurar y seleccionar manualmente las rutas que deben seguir los datos. Este tipo de enrutamiento es útil en situaciones en las que el diseño de la red o los parámetros esperados se mantienen constantes.

La naturaleza estática de este tipo de técnica de enrutamiento viene con desventajas esperadas, como la congestión de la red. Si bien los administradores pueden configurar rutas de respaldo en caso de que falle un enlace, el enrutamiento estático generalmente disminuye la adaptabilidad y flexibilidad de las redes, lo que resulta en un rendimiento de red limitado.

Enrutamiento dinámico

En el enrutamiento dinámico, los routers crean y actualizan tablas de enrutamiento en tiempo de ejecución en función de las condiciones reales de la red. Intentan encontrar la ruta más rápida desde el origen hasta el destino utilizando un protocolo de enrutamiento dinámico, que es un conjunto de reglas que crean, mantienen y actualizan la tabla de enrutamiento dinámico.

La mayor ventaja del enrutamiento dinámico es que se adapta a las condiciones cambiantes de la red, incluido el volumen de tráfico, el ancho de banda y la falla de la red.

Enrutamiento en una aplicación React

Al principio de este módulo, mencionamos que React no tiene un enrutador incorporado, razón por la cual no es reconocido como un framework de desarrollo web completo por una parte de la comunidad de desarrolladores. Sin embargo, existen varias bibliotecas de enrutamiento de terceros que se pueden utilizar con React, particularmente React Router se ha convertido en la más popular al día de hoy.

No obstante, debemos aclarar que, si bien React no implementa de manera nativa un mecanismo de enrutamiento, es posible implementar un enrutador simple utilizando el propio React. Si optamos por esta opción, debemos tener en cuenta que la implementación de un enrutador desde cero puede ser una tarea compleja, especialmente si deseamos implementar funcionalidades avanzadas, como la carga de módulos de forma dinámica, la protección de rutas, etc.

Además, existen diversas maneras de definir un mecanismo de enrutamiento propio.

Por ejemplo, podríamos utilizar un enfoque basado en condicionales, en el que se renderiza un componente diferente en función de la URL actual. Una implementación simple de este enfoque podría realizarse mediante la comparación de la propiedad pathname del objeto window.location, que contiene la URL actual de la página.

1
function App() {
2
const url = window.location.pathname;
3
4
if (url === '/products') {
5
return <Products />;
6
} else if (url === '/contact') {
7
return <Contact />;
8
} else if (currentPath === "/" || currentPath === "/home") {
9
return <Home />;
10
} else {
11
return <h1>Not Found</h1>;
12
}
13
};

Otra opción sería utilizar un enfoque basado en el estado, en el que se mantiene un estado local que almacena la URL actual y se actualiza cada vez que cambia la URL. En este caso, podríamos utilizar un componente de clase para mantener el estado local y suscribirnos a los cambios de la URL.

1
import { useState, useEffect} from 'react';
2
3
function App() {
4
const [currentPath, setCurrentPath] = useState(window.location.pathname);
5
6
useEffect(() => {
7
const onLocationChange = () => {
8
setCurrentPath(window.location.pathname);
9
};
10
11
window.addEventListener('popstate', onLocationChange);
12
13
return () => {
14
window.removeEventListener('popstate', onLocationChange);
15
};
16
}, []);
17
18
if (currentPath === "/products") {
19
return <Products />;
20
} else if (currentPath === "/contact") {
21
return <Contact />;
22
} else if (currentPath === "/" || currentPath === "/home") {
23
return <Home />;
24
} else {
25
return <h1>Not Found</h1>;
26
}
27
};

También, podríamos utilizar un enfoque basado en el contexto, en el que se pasa la URL actual a través del contexto de React. Entonces, podríamos emplear el patrón de proveedor de contexto de la siguiente manera:

1
import { createContext, useContext, useState, useEffect } from 'react';
2
3
const RouterContext = createContext();
4
5
function RouterProvider({ children }) {
6
const [currentPath, setCurrentPath] = useState(window.location.pathname);
7
8
useEffect(() => {
9
const onLocationChange = () => {
10
setCurrentPath(window.location.pathname);
11
};
12
13
window.addEventListener('popstate', onLocationChange);
14
15
return () => {
16
window.removeEventListener('popstate', onLocationChange);
17
};
18
}, []);
19
20
return (
21
<RouterContext.Provider value={currentPath}>
22
{children}
23
</RouterContext.Provider>
24
);
25
}
26
27
function useCurrentPath() {
28
return useContext(RouterContext);
29
}

Así, podríamos utilizar establecer el contexto en el componente raíz de la aplicación y consumirlo en cualquier componente secundario. Es decir, podríamos definir un router que se encargue de gestionar la URL actual mediante un proveedor de contexto, este será el componente Router que se encargará de renderizar el componente correspondiente en función de una ruta determinada.

1
function App() {
2
return (
3
<RouterProvider>
4
<Router />
5
</RouterProvider>
6
);
7
}
8
9
function Router() {
10
const currentPath = useCurrentPath();
11
12
if (currentPath === "/products") {
13
return <Products />;
14
} else if (currentPath === "/contact") {
15
return <Contact />;
16
} else if (currentPath === "/" || currentPath === "/home") {
17
return <Home />;
18
} else {
19
return <h1>Not Found</h1>;
20
}
21
}

Todos estos enfoques son válidos, y en menor medida, se pueden utilizar para aplicaciones pequeñas y sencillas. Es más, React Router utiliza un enfoque similar a estos dos enfoques, pero con la ventaja de que es una biblioteca probada y optimizada para el enrutamiento en aplicaciones React. Sin mencionar que existen muchas funcionalidades avanzadas que React Router proporciona, como la protección de rutas, la carga de módulos de forma dinámica, la gestión de rutas anidadas, etc.

Por estos motivos, durante el desarrollo de este tema nos enfocaremos en el uso de React Router para implementar el enrutamiento en nuestras aplicaciones React.

Bibliografía