Ivan González
Felipe Taborda Villegas
Gran aporte, gracias!
Héctor Haro Hermosillo
Excelente aporte, pero por que pasa esto.?
La navegación en una aplicación React no debería forzar al navegador a pedir un HTML nuevo cada vez que el usuario hace clic. Con los componentes Link y NavLink de react-router-dom puedes cambiar de ruta desde JavaScript, conservar el estado de la app y, además, resaltar la ruta activa con estilos dinámicos.
Por qué reemplazar la etiqueta a por Link en React Router
Usar etiquetas <a> para mover al usuario entre rutas tiene dos problemas concretos. Si trabajas con un hash router, te toca escribir manualmente el # en cada href, por ejemplo /#/blog en lugar de /blog. Y si usas un browser router, el clic dispara una nueva petición al servidor que vuelve a descargar todo el HTML, perdiendo la principal ventaja de una SPA.
La solución es delegar la navegación a JavaScript. React Router ya sabe qué tipo de router configuraste y se encarga de añadir el hash cuando hace falta o de actualizar la URL sin recargar la página [02:30].
¿Qué hace el componente Link de React Router? Cambia la URL desde JavaScript sin recargar el HTML y, según el router que tengas configurado, agrega el hash automáticamente. Se usa con la prop
toen lugar dehref.
Cómo se importa y se usa Link
El componente Link se importa desde react-router-dom y reemplaza directamente a <a> dentro del menú. La diferencia clave en sintaxis está en la prop: ya no es href, ahora es to [04:10].
jsx import { Link } from 'react-router-dom';
<li><Link to="/">Home</Link></li> <li><Link to="/blog">Blog</Link></li> <li><Link to="/profile">Profile</Link></li>Con esto, la navegación entre Home, Blog y Profile ocurre sin recargar la página y sin que tengas que preocuparte por el #.
Qué diferencia a NavLink de Link en React Router v6
NavLink funciona igual que Link: también recibe la prop to y renderiza un enlace. La ventaja es que te entrega información sobre si la ruta a la que apunta coincide con la ruta actual, lo cual sirve para resaltar el ítem del menú activo [06:20].
En la versión 6 de react-router-dom, las props className y style de NavLink aceptan una función en lugar de un string o un objeto. Esa función recibe un argumento llamado isActive, un booleano que te dice si el enlace corresponde a la URL actual.
jsx <NavLink to="/" style={({ isActive }) => ({ color: isActive ? 'red' : 'blue' })}
Home </NavLink>
Cuando estás en /, el enlace de Home se pinta rojo; al pasar a /blog, Home vuelve a azul y Blog se vuelve rojo. Así el usuario sabe en qué sección está, no solo por la URL.
¿Qué es isActive en NavLink? Es un parámetro booleano que React Router pasa a las funciones de
classNameostyledentro de unNavLink. Valetruecuando la ruta del enlace coincide con la ruta activa.
Cómo aplicar estilos dinámicos sin repetir código
Si copias la misma función de style en cada NavLink, el menú crece y el código se vuelve tedioso. La práctica recomendada es declarar un arreglo con la información de cada ruta y recorrerlo con .map para generar los enlaces [10:45].
Cómo renderizar un menú dinámico con un array de rutas
Define un arreglo de objetos donde cada objeto tenga las propiedades que cambian entre un enlace y otro: la ruta destino y el texto visible. Después usa .map para devolver un NavLink por cada elemento.
jsx const routes = [ { to: '/', text: 'Home' }, { to: '/blog', text: 'Blog' }, { to: '/profile', text: 'Profile' } ];
<ul> {routes.map((route) => ( <li key={route.to}> <NavLink to={route.to} style={({ isActive }) => ({ color: isActive ? 'red' : 'blue' })} > {route.text} </NavLink> </li> ))} </ul>Con este patrón, agregar una nueva sección al menú es solo añadir un objeto al arreglo. La función de estilos vive una sola vez y todos los enlaces heredan el comportamiento activo/inactivo.
Habilidades y conceptos que estás practicando
to para indicar la ruta destino [04:10].isActive para devolver diferentes valores en style o className [07:30].En la próxima clase vas a ver cómo crear rutas dinámicas, esas en las que una parte de la URL cambia y te permite renderizar componentes con la misma estructura pero contenido distinto. ¿Cómo estás organizando los menús de tus apps en React? Cuéntalo en los comentarios.
Ivan González
Felipe Taborda Villegas
Gran aporte, gracias!
Héctor Haro Hermosillo
Excelente aporte, pero por que pasa esto.?
Link vs. NavLink
Link
Estas nos ayudan a evitar el uso de las etiquetas <a> en caso de que deseemos pasar a otro contenido por medio de los NavHashes evitándonos el tener que volver a descargar el HTML nuevamente ya que cambia la URL manualmente.
// Importamos la etiqueta link import { Link } from 'react-router-dom'; function Menu() { return ( <nav> <ul> <li> {/* Si lo comparamos con las etiquetas <a> su href="" vendría siendo su parámetro to="", para estas no hay necesidad de colocar los hash */} <Link to="/">Home</Link> </li> <li> <Link to="/blog">Blog</Link> </li> <li> <Link to="/profile">Profile</Link> </li> </ul> </nav> ); }
Si lo probamos vemos que no nos está recargando la página y que nos envía y renderiza los componentes que deseamos.
NavLink
Este funciona exactamente igual que Link, incluso la propiedad to="/" también se usa en este componente, pero nos permite cierta flexibilidad, por ejemplo, cuando queramos darle una clase a nuestros componentes o estilos manuales nos permite que en nuestras propiedades className={} o style={} nos permite devolver una función.
// Importamos la etiqueta NavLink import { Link, NavLink } from 'react-router-dom'; function Menu() { return ( <nav> <ul> <li> <NavLink to="/" className={() => ''} style={() => ({background: '#fff'})} >Home</NavLink> </li> <li> <NavLink to="/blog">Blog</NavLink> </li> <li> <NavLink to="/profile">Profile</NavLink> </li> </ul> </nav> ); }
Estas mismas funciones nos permite enviar un parámetro llamado isActive, el cual nos permite saber si la ruta en la que se envía este componente es en la que estamos actualmente, y dependiendo de eso podemos darle una nueva clase o cambiar los estilos.
Para que esto funcione debemos pasarle a nuestro componente la propiedad end, si no, este siempre permanecerá activo.
<NavLink end // <-- style={({isActive }) => ({ color: isActive ? 'green' : 'red' }) } >Home</NavLink>
Si añadimos estas funciones una por una en los componentes tendremos bastante desorden, para arreglar este problema podemos crear un Array de posiciones.
const routes = []; routes.push({ to: '/', text: 'Home' }); routes.push({ to: '/blog', text: 'Blog' }); routes.push({ to: '/profile', text: 'Profile' });
Y ahora podemos recorrer este Array en el JSX para que se añadan los componentes de manera dinámica.
function Menu() { return ( <nav> <ul> {routes.map(route => ( <li> <NavLink to={route.to} end style={ ({ isActive }) => ({ color: isActive ? 'green' : 'red' })} > {route.text} </NavLink> </li> ))} </ul> </nav> ); }
De esta forma creamos un componente una sola vez y se añadirán a nuestra <nav> de manera dinámica, así en el futuro no tenemos que estas copiando y pegando código, solamente debemos crear otro elemento dentro del Array.
Fabio Erik Sosa Morinigo
Johangly Sucre
que genial, bien modularizado, me encanta.
Mi notas de esta clase:
Link:
NavLink:
isActive:
Gracias a este parámetro vamos a poder asignar estilos especiales a la etiqueta <NavLink> cuando la ruta le corresponda.
<NavLink style={({isActive})=>({ color: isActive ? 'red' : 'blue' })}to="/"> Home </NavLink>```
JOSE RANGEL
Hi banda. Como complemento, recuerden que cuando retornamos hijos a través de un .map (por ejemplo), cada hijo debe contar con la propiedad key o si no React se quejará. . En cada <li> que retornan al recorrer la lista de rutas, pueden colocarle como key el text o el indice. En mi caso le puse el text ya que es único para cada uno. .
routes.map(route => ( <li key={route.text}> <NavLink style={({ isActive }) => ({ color: isActive ? 'red' : 'blue' })} to={route.to} > {route.text} </NavLink> </li> ))
Seba Cardoso
Juan Pablo Cortés
NavLink tiene por defecto una clase con el nombre “active”. Pueden verlo desde el inspector de elementos del navegador.
asi se ve el inspector de elementos.
<aria-current="page" class="active" href="/#">HOME</a>
solo es agregarle el estilo en CSS y ya. a.active{ color: red; }
Funciona igual que el ejemplo del profe JuanDC, sin necesidad de utilizar arrow function.
Diego Reyes Cabrera
Buen dato!
Oscar Adrián Martínez Cavazos
No se olviden de desestructurar el isActive :) .
Max Andy Diaz Neyra
Código de la clase en TypeScript: Igual que el código del profesor con la siguiente diferencia:
interface IRoute { id: number, to: string, text: string } const routes:IRoute[] = [ { id: 1, to: '/', text: 'Home' }, { id: 2, to: '/blog', text: 'Blog' }, { id: 3, to: '/profile', text: 'Profile' } ];
Felipe de Jesús Isidro
y por que se hace un push por cada ruta en vez de crear un objeto con todos los elementos directamente?
Juan Castro
ProfesorDa exactamente lo mismo. En este caso se me hizo más cómodo hacer el push. :D
Geovanni Labastida Vázquez
Jose Luis Bedoya
Nav Link es muy bellako
Donovan RM
Rubén Ernesto Aragón Gil
Les comparto esto si les daba este error:
error:03000086:digital envelope routines::initialization error 'err_ossl_evp_unsupported'
Descarguen la nueva actualización de react-router, si tienen actualizado el nodejs:
npm install react-router-dom@6.4
Por último, métele una buena fixeada:
npm audit fix --force
Harold Zurita Simon
Rodrigo Andrés Moreno Pertúz
hay una forma mejor y mas limpia y es usando el hook de useRouter que ya viene instalado.
export function Menu() { const routes = useRoutes(routesDeJuanDc)
return <> {routes} </> }
y en App dentro del BrowserRouter va el componente <Menu/> y routesDeJuanDc es el array de objetos con las props de cada Route
Seba Cardoso
Julian Franco
Pero con este código tendrias que repetir código en el Menu.. obvio si se sigue la linea del teacher.
Sergio Olivo Orellana
Banda me funciono esto directo con el className para el css
Charles Castillo Rosas
Diego Reyes Cabrera
En la iteración de routes
routes.map( route => ( <li> //key? //...... </li> ) )
recordemos que React necesita un identificador único para no tener problemas en los renderizados. . Podríamos usar una propiedad “id” dentro de cada objeto del array routes.
routes.map( route => ( <li key={route.id}> //...... </li> ))
Si te pasa como a mi que la ruta "/" está siempre activa, solo necesitas pasar como parametro al NavLink la propiedad ++end++ Quedaría de la siguiente manera:
<NavLink style={({ isActive }) => ({ color: isActive ? "red" : "blue" })} to={route.to} end > {route.text} </NavLink>
Me encuentro en la versión 6.4.0 de react-router-dom
En mi caso solo separé en un archivo routes.js
const routes = [ { id: 1, to: '/', text: 'Home' }, { id: 2, to: '/blog', text: 'Blog' }, { id: 3, to: '/profile', text: 'Profile' } ]; export { routes };
y lo inserté dentro del Menú
import React from 'react'; import { Link, NavLink } from 'react-router-dom'; import { routes } from '../routes/routes.js'; const Menu = () => { return ( <> <ul className='bg-slate-800 flex justify-around'> {routes.map(route => ( <li key={route.id}> <NavLink className={({ isActive }) => ( isActive ? 'text-white' : 'text-black' )} to={route.to} >{route.text}</NavLink> </li> ))} </ul> </> ) } export { Menu };
routes.js
import { BlogPage } from './BlogPage'; import { HomePage } from './HomePage'; import { ProfilePage } from './ProfilePage'; const routes = [ { path: '/', element: <HomePage />, text: 'Home' }, { path: '/blog', element: <BlogPage />, text: 'Blog' }, { path: '/profile', element: <ProfilePage />, text: 'Profile' }, ] export default routes
App.js
import {HashRouter, useRoutes} from 'react-router-dom' import routes from './routes' import { Menu } from './Menu'; function Router() { const elements = useRoutes(routes) return elements } function App() { return ( <HashRouter> <Menu /> <Router /> </HashRouter> ); } export default App;
Menu.js
import React from 'react' import { NavLink } from 'react-router-dom' import routes from './routes' function Menu() { return ( <nav> <ul> {routes.map(route =>( <li> <NavLink style={({ isActive }) => ({ color: isActive ? 'red' : 'blue' })} to={route.path} > {route.text} </NavLink> </li>) )} </ul> </nav> ) } export { Menu }
Estoy usando la versión V6.15 de React Router DOM y así implemente el código. . Como estamos usando routes.map() para recorrer el array donde almacenamos nuestras rutas, tuve que usar un key attribute para <li>. . Ademas destructurar isActive y usar la propiedad end.
MENU COMPONENT
function Menu() { return ( <nav> <ul> {routes.map(route => ( <li key = {route.text}> <NavLink style ={ ( {isActive} ) => ({ color: isActive ? 'red' :'blue', }) } to={route.to} end > {route.text} </NavLink> </li> ))} </ul> </nav> ) }
. ROUTES
const routes = []; routes.push({ to : '/', text: 'Home' }); routes.push({ to : '/profile', text: 'Profile' }); routes.push({ to : '/blog', text: 'Blog' });
Bueno si te sale un error de consola es porque debes agregar una key para cada elemento de la lista, a mi me gusta trabajar como array de objetos de una vez:
<nav> <ul> {routes2.map(route => ( <li key={route.id}> <NavLink style={({ isActive }) => ({ color: isActive ? 'red' : 'blue', })} to={route.to} end > {route.text} </NavLink> </li> ))} </ul> </nav>
y el array:
const routes2 = [ { id:1, to: '/', text: 'Home', }, { id:2, to: '/blog', text: 'Blog', }, { id:3, to: '/profile', text: 'Profile', }, ];
Por si alguien le sirve el dato
Link vs. NavLink
.
Los componentes Link y NavLink nos ayudarán para que nuestra navegación no se transforme en nuevas peticiones al servidor para que descargue un nuevo HTML, sino que solo haga la navegación desde Javascript. Vamos a cambiar la etiqueta <a></a> con alguna de estas 2 opciones, y esto nos permite cambiar de url sin recargar la página y podamos mostrar la información correspondiente.
.
Eventualmente, si utilizamos la forma normal de crear enlaces no va a funcionar, puesto que estamos utilizando un HashRouter.
.
<nav> <ul> <li> <a href="/">Home</a> </li> <li> <a href="/blog">Blog</a> </li> <li> <a href="/profile">Profile</a> </li> </ul> </nav>
.
Si quisiéramos forzar su funcionamiento tendríamos que agregar manualmente los hashes a las rutas en la propiedad href. Sin embargo, esta etiqueta lo que hace es hacer una nueva petición al servidor para que se descargue otra vez el HTML y no queremos eso.
.
<nav> <ul> <li> <a href="/">Home</a> </li> <li> <a href="/#/blog">Blog</a> </li> <li> <a href="/#/profile">Profile</a> </li> </ul> </nav>
.
Vamos a utilizar la etiqueta <Link></Link> el cual funciona parecido a la etiqueta <a></a>, solo que en lugar de utilizar href utilizaremos la propiedad to.
.
function Menu() { return ( <nav> <ul> <li> <Link to="/">Home</Link> </li> <li> <Link to="/blog">Blog</Link> </li> <li> <Link to="/profile">Profile</Link> </li> </ul> </nav> ) } export { Menu };
.
Si navegamos esta vez no volverá a cargar la página, porque Link es un componente oficial de react-router-dom y sabe que tipo de router estamos utilizando. Esto significa que agrega o no el hash, dependiendo de lo que necesitemos. Incluso si estuviéramos trabajando con un BrowserRouter la navegación se realiza sin recargar la página, sino que va a ser el mismo HTML y con Javascript estamos cambiando los componentes que renderizamos en una ruta u otra.
.
function Menu() { return ( <nav> <ul> <li> <NavLink style={({ isActive }) => ({ color: isActive ? 'red' : 'blue', })} to="/">Home</NavLink> </li> <li> <NavLink style={({ isActive }) => ({ color: isActive ? 'red' : 'blue', })} to="/blog">Blog</NavLink> </li> <li> <NavLink style={({ isActive }) => ({ color: isActive ? 'red' : 'blue', })} to="/profile">Profile</NavLink> </li> </ul> </nav> ) } export { Menu };
.
Al utilizar NavLink se sigue utilizando to para la ruta, sin embargo este componente nos permite utilizar los componentes className y style. A travéz de estas propiedades, por ejemplo podemos utilizar funciones y recibir por destructuración al parámetro isActive que nos indica si nos encontramos en esa ruta en particular o si está activa, luego podemos realizar condicionales y devolver un estilo u otro dependiendo de ello.
.
function Menu() { return ( <nav> <ul> {routes.map(route => ( <li> <NavLink style={({ isActive }) => ({ color: isActive ? 'red' : 'blue', })} to={route.to} > {route.text} </NavLink> </li> ))} </ul> </nav> ); } const routes = []; routes.push({ to: '/', text: 'Home', }); routes.push({ to: '/blog', text: 'Blog', }); routes.push({ to: '/profile', text: 'Profile', }); export { Menu };
.
Como pudimos observar habían partes de código repetido, por lo cual es conveniente crear un arreglo de rutas donde iremos insertando unos objetos con las propiedades to y text, que nos servirán para renderizar los items de nuestra lista de manera dinámica mediante un map y utilizando el style una sola vez.
Lo que tu dices es que se puede hacer de esta forma:
import {HashRouter, useRoutes} from 'react-router-dom' import { BlogPage } from './BlogPage'; import { HomePage } from './HomePage'; import { Menu } from './Menu'; import { ProfilePage } from './ProfilePage'; function Router() { const routes = [ { path: '/', element: <HomePage />, }, { path: '/blog', element: <BlogPage />, }, { path: '/profile', element: <ProfilePage />, }, ] const elements = useRoutes(routes) return elements } function App() { return ( <HashRouter> <Menu /> <Router /> </HashRouter> ); } export default App;
useRoutes