Articolo originale: React Router Tutorial – How to Render, Redirect, Switch, Link, and More, With Code Examples

Se hai appena iniziato con React, probabilmente ti stai arrovellando sul concetto di Applicazione a Pagina Singola (Single Page Application).

Tradizionalmente l'instradamento funziona in questo modo: supponiamo che digiti /contact come URL. Il browser eseguirà una richiesta GET al server, il quale restituirà una pagina HTML come risposta.

Tuttavia con il paradigma della Applicazione a Pagina Singola, tutte le richieste via URL sono servite usando il codice lato client.

Applicando questo nel contesto di React, ciascuna pagina sarà un componente React. React-Router cerca una corrispondenza con l'URL e carica il componente per quella particolare pagina.

Tutto accade così velocemente e perfettamente, che l'utente ottiene nel browser la stessa esperienza che avrebbe utilizzando una app nativa. Non ci sono pagine vuote lampeggianti durante la transizione tra le rotte.

In questo articolo, imparerai coma usare React-Router e i suoi componenti per creare una Applicazione a Pagina Singola. Quindi apri il tuo editor preferito e iniziamo.

Impostare il Progetto

Crea un nuovo progetto React eseguendo il seguente comando.

yarn create react-app react-router-demo

Userò yarn per l'installazione delle dipendenze ma puoi usare anche npm.

Ora installiamo react-router-dom.

yarn add react-router-dom

Per lo stile dei componenti userò il framework CSS Bulma, quindi aggiungiamolo.

yarn add bulma

Successivamente, importa bulma.min.css nel file index.js ed elimina tutto il codice boilerplate dal file App.js.

import "bulma/css/bulma.min.css";

Ora che hai il progetto impostato iniziamo a creare alcuni componenti per rappresentare le pagine.

Creare i Componenti di Pagina

Crea una directory pages all'interno della cartella src, dove verranno inseriti tutti i componenti di pagina.

Per questa dimostrazione, crea tre pagine - Home, About, e Profile.

Incolla il seguente codice all'interno dei componenti Home e About.

// pages/Home.js

import React from "react";

const Home = () => (
  <div>
    <h1 className="title is-1">This is the Home Page</h1>
    <p>
      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.
    </p>
  </div>
);

export default Home;
// pages/About.js

import React from "react";

const About = () => (
  <div>
    <h1 className="title is-1">This is the About Page</h1>
    <p>
      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.
    </p>
  </div>
);

export default About;

Creeremo la pagina Profile successivamente.

Creare il Componente Navbar

Iniziamo creando la barra di navigazione per l'app, la quale utilizzerà il componente <NavLink /> di react-router-dom.

Crea una directory chiamata components all'interno della cartella src.

// components/Navbar.js

import React, { useState } from "react";
import { NavLink } from "react-router-dom";

const Navbar = () => {
  const [isOpen, setOpen] = useState(false);
  return ( 
  	<nav
      className="navbar is-primary"
      role="navigation"
      aria-label="main navigation"
    >
      <div className="container">
      	{/* ... */}
      </div>
    </nav>
  );
 };
 
 export default Navbar;

La variabile di stato isOpen sarà usata per attivare il menu su dispositivi mobili o tablet.

Aggiungiamo quindi il menu "hamburger".

const Navbar = () => {
  const [isOpen, setOpen] = useState(false);
  return ( 
  	<nav
      className="navbar is-primary"
      role="navigation"
      aria-label="main navigation"
    >
      <div className="container">
      <div className="navbar-brand">
          <a
            role="button"
            className={`navbar-burger burger ${isOpen && "is-active"}`}
            aria-label="menu"
            aria-expanded="false"
            onClick={() => setOpen(!isOpen)}
          >
            <span aria-hidden="true"></span>
            <span aria-hidden="true"></span>
            <span aria-hidden="true"></span>
          </a>
        </div>
      	{/* ... */}
      </div>
    </nav>
  );
 };

Per aggiungere il link al menu utilizza il componente <NavLink /> di react-router-dom.

Il componente NavLink fornisce un modo dichiarativo per navigare nell'applicazione. È simile al componente Link, tranne per il fatto che è possibile applicare uno stile che lo evidenzia se il link è attivo.

Per specificare verso quale rotta navigare, usa la proprietà to assegnandole come valore il nome del percorso.
Nel caso il link sia attualmente attivo, la proprietà activeClassName aggiungerà una classe per evidenziarlo.

<NavLink
    className="navbar-item"
    activeClassName="is-active"
    to="/"
    exact
>
	Home
</NavLink>

Nel browser, il componente NavLink viene presentato come un tag <a> con un valore dell'attributo href corrispondente a quello che è stato passato alla proprietà to .

Inoltre occorre specificare qui la proprietà exact in modo che corrisponda precisamente con l'URL.

Aggiungi tutti i link e termina il componente Navbar.

import React, { useState } from "react";
import { NavLink } from "react-router-dom";

const Navbar = () => {
  const [isOpen, setOpen] = useState(false);
  return (
    <nav
      className="navbar is-primary"
      role="navigation"
      aria-label="main navigation"
    >
      <div className="container">
        <div className="navbar-brand">
          <a
            role="button"
            className={`navbar-burger burger ${isOpen && "is-active"}`}
            aria-label="menu"
            aria-expanded="false"
            onClick={() => setOpen(!isOpen)}
          >
            <span aria-hidden="true"></span>
            <span aria-hidden="true"></span>
            <span aria-hidden="true"></span>
          </a>
        </div>

        <div className={`navbar-menu ${isOpen && "is-active"}`}>
          <div className="navbar-start">
            <NavLink className="navbar-item" activeClassName="is-active" to="/">
              Home
            </NavLink>

            <NavLink
              className="navbar-item"
              activeClassName="is-active"
              to="/about"
            >
              About
            </NavLink>

            <NavLink
              className="navbar-item"
              activeClassName="is-active"
              to="/profile"
            >
              Profile
            </NavLink>
          </div>

          <div className="navbar-end">
            <div className="navbar-item">
              <div className="buttons">
                <a className="button is-white">Log in</a>
              </div>
            </div>
          </div>
        </div>
      </div>
    </nav>
  );
};

export default Navbar;

Se hai notato, qui ho aggiunto un bottone Login. Torneremo sul componente Navbar quando discuteremo le rotte protette successivamente in questo articolo.

Presentare le Pagine

Ora che il componente Navbar è stato impostato aggiungiamolo alla pagina e iniziamo a presentare le pagine.

Visto che la barra di navigazione è un componente comune a tutte le pagine, invece di chiamare il componente in ciascuna di esse, sarà un approccio migliore presentare Navbar secondo un layout comune a tutte le pagine.

// App.js

function App() {
  return (
    <>
      <Navbar />
      <div className="container mt-2" style={{ marginTop: 40 }}>
        {/* Render the page here */}
      </div>
    </>
  );
}

Ora aggiungi i componenti di pagina all'interno del contenitore.

// App.js

function App() {
  return (
    <>
      <Navbar />
      <div className="container mt-2" style={{ marginTop: 40 }}>
        <Home />
      	<About />
      </div>
    </>
  );
}

Se controlli il risultato adesso, noterai che i componenti di pagina Home e About sono presentati entrambi all'interno della pagina. Questo perché non abbiamo ancora aggiunto alcuna logica di instradamento.

Per aggiungere la capacità di navigare tra i componenti devi importare il componente di React Router BrowserRouter. Tutto quello che devi fare è racchiudere tutti i componenti di pagina all'interno del componente BrowserRouter. Questo consentirà a tutti i componenti di pagina di avere una logica di instradamento. Perfetto!

Ma ancora una volta, nulla cambia per quanto riguarda il risultato – vedrai ancora entrambe le pagine presentate. Dovrai presentare il componente di pagina solo se l'URL corrisponde a un particolare percorso. È qui che entra in gioco il componente di React Router Route.

Il componente Route ha una proprietà path che accetta il percorso della pagina e il componente di pagina dovrebbe essere inserito all'interno di Route, come mostrato qui sotto.

<Route path="/about">
  <About />
</Route>

Facciamo lo stesso per il componente Home .

<Route exact path="/">
  <Home />
</Route>

La proprietà exact qui sopra dice al componente Route di cercare corrispondenza esatta per il percorso. Senza aggiungere la proprietà exact al percorso /, ci sarebbe corrispondenza con tutte le rotte che iniziano con / compreso /about.

Se adesso verifichi i risultati, vedrai ancora presentati entrambi i componenti, ma se vai su /about, noterai che solo il componente About viene presentato. Questo comportamento si verifica in quanto il router continua a cercare corrispondenza dell'URL con le rotte anche dopo che ne ha già trovata una.

Dobbiamo dire al router di interrompere la ricerca di corrispondenza una volta che ha trovato una rotta che corrisponde. Questo si ottiene usando il componente Switch di React Router.

function App() {
  return (
    <BrowserRouter>
      <Navbar />
      <div className="container mt-2" style={{ marginTop: 40 }}>
        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/about">
            <About />
          </Route>
        </Switch>
      </div>
    </BrowserRouter>
  );
}

Ed ecco fatto! Hai configurato con successo l'instradamento nella tua app React.

Rotte Protette e Redirezione

Lavorando su applicazioni per il mondo reale, ci saranno alcune rotte che sono dipendenti da un sistema di autenticazione. Avrai rotte o pagine che saranno accessibili solamente da un utente connesso. In questa sezione, imparerai come implementare queste rotte.

Per favore nota che non andrò a creare alcun form di accesso o un qualsiasi servizio back-end per autenticare l'utente. In una applicazione reale, non implementeresti l'autenticazione nel modo qui mostrato.

Creiamo il componente di pagina Profile al quale dovrebbe avere accesso solo un utente autenticato.

// pages/Profile.js

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

const Profile = () => {
  const { name } = useParams();
  return (
    <div>
      <h1 className="title is-1">This is the Profile Page</h1>
      <article className="message is-dark" style={{ marginTop: 40 }}>
        <div className="message-header">
          <p>{name}</p>
        </div>
        <div className="message-body">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.{" "}
          <strong>Pellentesque risus mi</strong>, tempus quis placerat ut, porta
          nec nulla. Vestibulum rhoncus ac ex sit amet fringilla. Nullam gravida
          purus diam, et dictum <a>felis venenatis</a> efficitur. Aenean ac{" "}
          <em>eleifend lacus</em>, 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.
        </div>
      </article>
    </div>
  );
};

Utilizzando i parametri di rotta recupereremo dall'URL il nome utente.

Aggiungi la rotta Profile all'interno del router.

<Route path="/profile/:name">
  <Profile />
</Route>

Attualmente si può accedere direttamente alla pagina Profile. Facciamo in modo che sia una rotta autenticata creando un componente di livello più alto (Higher-Order component - HOC) per incapsulare la logica di autenticazione.

const withAuth = (Component) => {
  const AuthRoute = () => {
    const isAuth = !!localStorage.getItem("token");
    // ...
  };

  return AuthRoute;
};

Per determinare se un utente sia o meno autenticato, recupera il token di autenticazione che è conservato nel browser quando l'utente si connette. Se l'utente non è autenticato, reindirizza l'utente alla pagina Home. Il componente di React Router Redirect può essere usato per reindirizzare l'utente verso un altro percorso.

const withAuth = (Component) => {
  const AuthRoute = () => {
    const isAuth = !!localStorage.getItem("token");
    if (isAuth) {
      return <Component />;
    } else {
      return <Redirect to="/" />;
    }
  };

  return AuthRoute;
};

Puoi anche passare altre informazioni sull'utente come il nome e il suo ID passandole come proprietà verso il componente incapsulato.

Successivamente usa l'HOC withAuth nel componente Profile.

import withAuth from "../components/withAuth";

const Profile = () => {
 // ...
}

export default withAuth(Profile);

Ora se provi a visitare /profile/JohnDoe, sarai reindirizzato alla pagina Home. Questo perché il token di autenticazione non è ancora stato impostato e conservato nel browser.

Bene, torniamo al componente Navbar e aggiungiamo le funzionalità di connessione e disconnessione. Quando l'utente è autenticato viene mostrato il pulsante Logout per disconnettersi e quando l'utente non è ancora connesso viene mostrato il pulsante Login per connettersi.

// components/Navbar.js

const Navbar = () => {
	// ...
    return (
    	<nav
          className="navbar is-primary"
          role="navigation"
          aria-label="main navigation"
        >
        <div className="container">
        	{/* ... */}
            <div className="navbar-end">
            <div className="navbar-item">
              <div className="buttons">
                {!isAuth ? (
                  <button className="button is-white" onClick={loginUser}>
                    Log in
                  </button>
                ) : (
                  <button className="button is-black" onClick={logoutUser}>
                    Log out
                  </button>
                )}
              </div>
            </div>
          </div>
        </div>
        </nav>
    );
}

Quando l'utente fa click sul pulsante Login, imposta un token fittizio nel localStorage e reindirizza l'utente alla pagina Profile.

In questo caso, tuttavia, non possiamo usare il componente Redirect – dobbiamo reindirizzare l'utente programmaticamente. Dato che il token per l'autenticazione costituisce un dato sensibile, in genere è conservato in un cookie per ragioni di sicurezza.

React Router ha un componente HOC withRouter che inserisce l'oggetto history nelle proprietà del componente per sfruttare l'API History. Passa anche al componente incapsulato le proprietà match e location aggiornate.

// 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);

E voilà! Questo è tutto. Hai conquistato anche la terra delle rotte autenticate.

Puoi trovare la dimostrazione live qui e il codice completo in questo repository come riferimento.

Conclusione

Spero che ormai ti sia fatto una buona idea di come funziona l'instradamento lato client in generale e come implementarlo in React usando la libreria React Router.

In questa guida, hai imparato a conoscere i componenti vitali di React Router quali Route, withRouter, Link e così via, assieme ad alcuni concetti avanzati come le rotte autenticate, che sono richieste per costruire un'applicazione.

Consulta la documentazione di  React Router per ottenere una panoramica più dettagliata di ciascuno dei componenti.