Si acaba de comenzar con React, probablemente aún esté comprendiendo todo el concepto de aplicación de una sola página.
Tradicionalmente, el enrutamiento funciona así: digamos que escribe /contact
la URL. El navegador realizará una solicitud GET al servidor y el servidor devolverá una página HTML como respuesta.
Pero, con el nuevo paradigma de aplicación de página única, todas las solicitudes de URL se atienden utilizando el código del lado del cliente.
Aplicando esto en el contexto de React, cada página será un componente de React. React-Router coincide con la URL y carga el componente para esa página en particular.
Todo sucede tan rápido y sin problemas que el usuario obtiene una experiencia similar a una aplicación nativa en el navegador. No hay una página en blanco llamativa entre las transiciones de ruta.
En este artículo, aprenderá a usar React-Router y sus componentes para crear una aplicación de página única. Así que abre tu editor de texto favorito y comencemos.
Configurar el proyecto
Crea un nuevo proyecto de React ejecutando el siguiente comando.
yarn create react-app react-router-demo
Usaré yarn para instalar las dependencias, pero también puedes usar npm.
A continuación, instalemos react-router-dom
.
yarn add react-router-dom
Para diseñar los componentes, usaré el marco CSS de Bulma. Así que agreguemos eso también.
yarn add bulma
A continuación, importe bulma.min.css
en el index.js
archivo y limpie todo el código repetitivo del App.js
archivo.
import "bulma/css/bulma.min.css";
Ahora que tiene el proyecto configurado, comencemos por crear algunos componentes de página.
Crear los componentes de la página
Cree un directorio de páginas dentro de la carpeta src donde estacionaremos todos los componentes de la página.
Para esta demostración, cree tres páginas: Inicio, Acerca de y Perfil.
Pegue lo siguiente dentro de los componentes Inicio y Acerca de.
// pages/Home.js import React from "react"; const Home = () => ( This is the Home Page
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras gravida, risus at dapibus aliquet, elit quam scelerisque tortor, nec accumsan eros nulla interdum justo. Pellentesque dignissim, sapien et congue rutrum, lorem tortor dapibus turpis, sit amet vestibulum eros mi et odio.
); export default Home;
// pages/About.js import React from "react"; const About = () => ( This is the About Page
Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Duis consequat nulla ac ex consequat, in efficitur arcu congue. Nam fermentum commodo egestas.
); export default About;
Crearemos la página de perfil más adelante en el artículo.
Crear el componente Navbar
Comencemos creando la barra de navegación para nuestra aplicación. Este componente hará uso del componente de
react-router-dom
.
Cree un directorio llamado "componentes" dentro de la carpeta src.
// components/Navbar.js import React, { useState } from "react"; import { NavLink } from "react-router-dom"; const Navbar = () => { const [isOpen, setOpen] = useState(false); return ( {/* ... */} ); }; export default Navbar;
La isOpen
variable de estado se utilizará para activar el menú en dispositivos móviles o tabletas.
Entonces agreguemos el menú de hamburguesas.
const Navbar = () => { const [isOpen, setOpen] = useState(false); return ( setOpen(!isOpen)} > {/* ... */} ); };
Para agregar el enlace en el menú, use el componente por
react-router-dom
.
El NavLink
componente proporciona una forma declarativa de navegar por la aplicación. Es similar al Link
componente, excepto que puede aplicar un estilo activo al enlace si está activo.
Para especificar a qué ruta navegar, use el to
accesorio y pase el nombre de la ruta.
El activeClassName
accesorio agregará una clase activa al enlace si está activo actualmente.
Home
En el navegador, el NavLink
componente se representa como una etiqueta con un
href
valor de atributo que se pasó en la to
propiedad.
Además, aquí debe especificar el exact
accesorio para que coincida con precisión con la URL.
Agrega todos los enlaces y termina el Navbar
componente.
import React, { useState } from "react"; import { NavLink } from "react-router-dom"; const Navbar = () => { const [isOpen, setOpen] = useState(false); return ( setOpen(!isOpen)} > Home About Profile Log in ); }; export default Navbar;
Si observa aquí, agregué un botón de inicio de sesión. Volveremos al Navbar
componente nuevamente cuando analicemos las rutas protegidas más adelante en la guía.
Renderizando las páginas
Ahora que el Navbar
componente está configurado, agreguemos eso a la página y comencemos con la representación de las páginas.
Dado que la barra de navegación es un componente común en todas las páginas, en lugar de llamar al componente en cada componente de la página, será un mejor enfoque representar el Navbar
en un diseño común.
// App.js function App() { return ( {/* Render the page here */} ); }
Ahora, agregue los componentes de la página dentro del contenedor.
// App.js function App() { return ( ); }
Si consulta los resultados ahora, notará que tanto el componente de la página Inicio como el de Acerca de se representan en la página. Eso es porque aún no hemos agregado ninguna lógica de enrutamiento.
You need to import the BrowserRouter
component from React Router to add the ability to route the components. All you need to do is wrap all the page components inside of the BrowserRouter
component. This will enable all the page components to have the routing logic. Perfect!
But again, nothing's going to change with the results – you are still going to see both the pages rendered. You need to render the page component only if the URL matches a particular path. That's where the Route
component from React Router comes into play.
The Router
component has a path
prop that accepts the page's path, and the page component should be wrapped with the Router
, as shown below.
So let's do the same for the Home
component.
The exact
prop above tells the Router
component to match the path exactly. If you don't add the exact
prop on the /
path, it will match with all the routes starting with a /
including /about
.
If you go check out the results now, you'll still see both the components rendered. But, if you go to /about
, you'll notice that only the About
component gets rendered. You see this behavior because the router keeps matching the URL with the routes even after it had matched a route already.
We need to tell the router to stop matching further once it matches a route. This is done using the Switch
component from React Router.
function App() { return ( ); }
There you go! You have successfully configured routing in your React app.
Protected Routes and Redirect
When working on Real-world applications, you will have some routes behind an authentication system. You are going to have routes or pages that can only be accessed by a logged-in user. In this section, you'll learn how to go about implementing such routes.
Please note that I'm not going to create any login form or have any back-end service authenticate the user. In a real application, you wouldn't implement authentication the way demonstrated here.
Let's create the Profile page component that should only be accessed by the authenticated user.
// pages/Profile.js import { useParams } from "react-router-dom"; const Profile = () => { const { name } = useParams(); return ( This is the Profile Page
{name}
Lorem ipsum dolor sit amet, consectetur adipiscing elit.{" "} Pellentesque risus mi, tempus quis placerat ut, porta nec nulla. Vestibulum rhoncus ac ex sit amet fringilla. Nullam gravida purus diam, et dictum felis venenatis efficitur. Aenean ac{" "} eleifend lacus, in mollis lectus. Donec sodales, arcu et sollicitudin porttitor, tortor urna tempor ligula, id porttitor mi magna a neque. Donec dui urna, vehicula et sem eget, facilisis sodales sem. ); };
We will grab the user's name from the URL using route parameters.
Add the Profile route into the router.
Currently the profile page can be accessed directly. So to make it an authenticated route, create a Higher-Order component (HOC) to wrap the authentication logic.
const withAuth = (Component) => { const AuthRoute = () => { const isAuth = !!localStorage.getItem("token"); // ... }; return AuthRoute; };
To determine if a user is authenticated or not, grab the authentication token that is stored in the browser when the user logs in. If the user is not authenticated, redirect the user to the Home page. The Redirect
component from React Router can be used to redirect the user to another path.
const withAuth = (Component) => { const AuthRoute = () => { const isAuth = !!localStorage.getItem("token"); if (isAuth) { return ; } else { return ; } }; return AuthRoute; };
You can also pass in other user information like name and user ID using props to the wrapped component.
Next, use the withAuth
HOC in the Profile component.
import withAuth from "../components/withAuth"; const Profile = () => { // ... } export default withAuth(Profile);
Now, if you try to visit /profile/JohnDoe
, you will get redirected to the Home page. That's because the authentication token is not yet set in your browser storage.
Alright, so, let's return to the Navbar
component and add the login and logout functionalities. When the user is authenticated, show the Logout button and when the user is not logged in show the Login button.
// components/Navbar.js const Navbar = () => { // ... return ( {/* ... */} {!isAuth ? ( Log in ) : ( Log out )} ); }
When the user clicks on the login button, set a dummy token in the local storage, and redirect the user to the profile page.
But we cannot use the Redirect component in this case – we need to redirect the user programmatically. Sensitive tokens used for authentication are usually stored in cookies for security reasons.
React Router has a withRouter
HOC that injects the history
object in the props of the component to leverage the History API. It also passes the updated match
and location
props to the wrapped component.
// components/Navbar.js import { NavLink, withRouter } from "react-router-dom"; const Navbar = ({ history }) => { const isAuth = !!localStorage.getItem("token"); const loginUser = () => { localStorage.setItem("token", "some-login-token"); history.push("/profile/Vijit"); }; const logoutUser = () => { localStorage.removeItem("token"); history.push("/"); }; return ( {/* ... */} ); }; export default withRouter(Navbar);
And voilà! That's it. You have conquered the land of authenticated routes as well.
Check out the live demo here and the complete code in this repo for your reference.
Conclusion
I hope by now you have a fair idea of how client-side routing works in general and how to implement routing in React using the React Router library.
In this guide, you learned about the vital components in React Router like Route
, withRouter
, Link
, and so on, along with some advanced concepts like authenticated routes, that are required to build an application.
Do check out the React Router docs to get a more detailed overview of each of the components.