ansango / wiki
 ·  7 min de lectura

Router

Guía completa de React Router: navegación, rutas, hooks, BrowserRouter, HashRouter y navegación programática

Para instalarla ejecutamos lo siguiente en terminal:

npm install react-router-dom

o si utilizamos yarn

yarn add react-router-dom

Estructura del router

Para poder utilizar React Router tenemos que meter en nuestra aplicación lo siguiente:

Podemos ver una estructura básica en el siguiente ejemplo:

const App = () => {
  return (
    <div id="main" className="App">
      <Router>
        <div>
          <nav>
            <ul>
              <li>
                <Link to="/">Home</Link>
              </li>
              <li>
                <Link to="/todo">TodoList</Link>
              </li>
            </ul>
          </nav>
          <Switch>
            <Route path="/todo">
              <Todo />
            </Route>
            <Route path="/">
              <Home />
            </Route>
          </Switch>
        </div>
      </Router>
    </div>
  );
};

Tipos de Router

Browser Router

Se utiliza cuando en nuestra aplicación utilizamos paths regulares:

<BrowserRouter>

// Regular paths

// https://example.com/your/page

Hash Router

<HashRouter>

// Paths with hash

// https://example.com/#/your/page

La principal diferencia entre los dos es la manera en la que las URLs son almacenadas y la manera de comunicarse con el web server. Pero a nivel funcional (navegación entre rutas), funcionan de la misma manera.

Route matchers

Son los componentes que comprueban la url que tenemos en el navegador y que observan qué se tiene y qué no se tiene que renderizar.

Cuando se renderiza el <Switch>, se busca el componente <Route> que encaje con la url actual. Cuando se encuentra ese <Route>, se renderiza y se ignoran el resto de paths.

Por esta razón debemos de situar antes la ruta específica por delante de la más genérica.

<Switch>
  <Route path="/about">
    <About />
  </Route>
  <Route path="/contact/:id">
    <Contact />
  </Route>
  <Route path="/contact">
    <AllContacts />
  </Route>
  <Route path="/">
    <Home />
  </Route>
</Switch>

Existen tres tipos de componentes para navegar entre rutas:

<Link to="/">Home</Link>
<NavLink to="/react" activeClassName="hurray">
  React
</NavLink>
<Redirect to="/login" />

Tenemos tres objetos para poder realizar está navegación:

History

Este objeto nos permite navegar en la aplicación de manera programática. Este objeto nos da los siguientes métodos y propiedades (React Router funciona como un stack, y hay que tenerlo en cuenta a la hora de utilizar los métodos, ya que añaden al stack esas acciones):

Location

Como veíamos este objeto pertenece a History y nos proporciona información acerca de dónde está la aplicación ahora, dónde quieres que vaya, o incluso dónde estaba. Este objeto nos trae lo siguiente:

{
  pathname: '/somewhere', // donde estamos actualmente
  search:'?some=search-string', // query string si nos pasan algo
  hash:'#howdy', // en caso de tener algún hash
  state: { // pasar información entre rutas
      [userDefined]: true
  }
}

Match

Un objeto match contiene información sobre la coincidencia de una <Route> con la URL. Los objetos match contienen las siguientes propiedades:

{
  isExact: true, // nos indica si la URL se casa completamente
  params: { projectId: "1", version: "1.0.0", modelId: "30" }, // objeto con todos los parámetros de la URL
  path: "/projects/:projectId/version/:version/models/:modelId", // El patrón de parámetros seguidos para obtener la ruta
  url: "/projects/1/version/1.0.0/models/30", // la URL completa
}

Hooks

¿Cómo obtenemos los objetos anteriores? React Router viene con algunos hooks que te permiten acceder al estado del router y realizar la navegación desde dentro de tus componentes.

useHistory

El hook useHistory te da acceso a la instancia del historial que puedes usar para navegar.

import { useHistory } from "react-router-dom";

const HomeButton = () => {
  let history = useHistory();

  function handleClick() {
    history.push("/home");
  }

  return (
    <button type="button" onClick={handleClick}>
      Go home
    </button>
  );
};

useLocation

El hook useLocation devuelve el objeto location que representa la URL actual. Puedes pensar en él como un useState que devuelve una nueva ubicación cada vez que la URL cambia.

import { useLocation } from "react-router-dom";

const usePageViews = () => {
  let location = useLocation();
  useEffect(() => {
    alert(`Changed to ${location.pathname}`);
  }, [location]);
};

useParams

useParams devuelve un objeto de pares clave/valor de parámetros URL. Se utiliza para acceder a match.params de la <Route> actual.

import {
  BrowserRouter as Router,
  Switch,
  Route,
  useParams,
} from "react-router-dom";

const BlogPost = () => {
  let { id } = useParams();
  return <div>Now showing post {id}</div>;
};

ReactDOM.render(
  <Router>
    <Switch>
      <Route exact path="/">
        <HomePage />
      </Route>
      <Route path="/blog/:id">
        <BlogPost />
      </Route>
    </Switch>
  </Router>,
  node
);

useRouteMatch

El hook useRouteMatch intenta hacer coincidir la URL actual de la misma manera que lo haría un <Route>. Es útil sobre todo para acceder a los datos de coincidencia sin tener que renderizar una <Route>.

import { useRouteMatch } from "react-router-dom";

const BlogPost = () => {
  let match = useRouteMatch("/blog/:slug");

  return <div>The pattern is {match.path}</div>;
};

Ejemplo completo

import React from "react";

import "./App.css";
import Header from "./components/Header";
import SubHeader from "./components/SubHeader";
import Content from "./components/Content";
import Footer from "./components/Footer";
import Help from "./components/Help";
import AllergenDetail from "./components/AllergenDetail";
import { UserProvider } from "./state/UserContext";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";

const App = () => {
  const title = "React Training";

  return (
    <div className="App">
      {/* Router */}
      <Router>
        <UserProvider>
          <Header title={title} />
          <SubHeader />
          {/* Switch */}
          <Switch>
            {/* Routes */}
            <Route path="/allergen/:name">
              <AllergenDetail />
            </Route>
            <Route path="/allergen">
              <Content />
            </Route>
            <Route path="/help">
              <Help />
            </Route>
            <Route path="/">
              <div>
                <h1> Welcome!</h1>
              </div>
            </Route>
          </Switch>
          <Footer />
        </UserProvider>
      </Router>
    </div>
  );
};

export default App;
import React from "react";
import { useUser } from "../state/UserContext";
import { Link, NavLink } from "react-router-dom";
const Header = ({ title, style = {} }) => {
  const { changeUser } = useUser();
  return (
    <header style={style} onClick={changeUser}>
      <h1>{title}</h1>
      <Nav />
      <UserBox />
    </header>
  );
};

const Nav = () => {
  return (
    <nav>
      <ul>
        <NavLink to="/" exact>
          Home 🏠
        </NavLink>
        <NavLink to="/allergen">Menú 🍕</NavLink>
        <NavLink to="/help">Help 🚧</NavLink>
      </ul>
    </nav>
  );
};

const UserBox = () => {
  const { userName } = useUser();
  return <h3>{userName}</h3>;
};

export default Header;
import React from "react";
import { useParams } from "react-router-dom";

const AllergenDetail = (props) => {
  const params = useParams();
  return (
    <div>
      <h1>{params.name}</h1>
      <p>
        We don't have any informaion about this allergen yet. But we will get it
        soon 😉
      </p>
    </div>
  );
};

export default AllergenDetail;
import React from "react";
import List from "./List";
import { allergens } from "../data";

const Content = () => {
  const [menu, setMenu] = React.useState([]);
  const handleAdd = (id) => {
    setMenu((pv) => [...pv, allergens.find((a) => a.id === id)]);
  };
  return (
    <div className="flex w-100">
      <div className="w-50">
        <h1>Available:</h1>
        <List items={allergens} onSelect={handleAdd} showButtons />
      </div>
      <div className="w-50">
        <h1>Menu:</h1>
        <List items={menu} />
      </div>
    </div>
  );
};

export default Content;
import React from "react";
import { useHistory } from "react-router";
import Allergen from "./Allergen";

const List = (props) => {
  const history = useHistory();
  const handleSelect = (id) => {
    props.onSelect(id);
  };

  return (
    <div>
      {props.items.map((a) => (
        <Allergen
          key={a.id}
          name={a.text}
          icon={a.icon}
          hot={a.yummy}
          showButton={props.showButtons}
          onSelect={() => handleSelect(a.id)}
          onClick={() => history.push(`/allergen/${a.text}`)}
        />
      ))}
    </div>
  );
};

export default List;
import React from "react";
import { Link, Route, Switch } from "react-router-dom";
import HelpDetail from "./HelpDetail";

const Help = (props) => {
  return (
    <div>
      <h1>Help</h1>
      <Link to="/help/react">React Help</Link>
      <Link to="/help/allergen">Allergen Help</Link>
      <Switch>
        <Route path="/help/:name">
          <HelpDetail />
        </Route>
      </Switch>
    </div>
  );
};

export default Help;
import React from "react";
import { useParams } from "react-router-dom";

const HelpDetail = (props) => {
  const params = useParams();
  return (
    <div>
      <h3>{params.name}</h3>
      <p>Here we are going to show {params.name} help</p>
    </div>
  );
};

export default HelpDetail;

❤️ Espero que te haya gustado la entrada.