Router

  • React Router et permet renderitzar un component concret en funció del "path" de la url i navegar entre components.

    Introducció

    React Router et permet renderitzar un component concret en funció del “path” de la url.

    Això s’aconsegueix creant un conjunt de rutes, on cada ruta és un “mapping” entre el path d’una URL i un component:

    route

    Path

    Component

    Entorn de treball

    Crea un projecte amb Bun

    bun create vite router --template react-swc-ts
    cd router
    bun update

    Instal·la la biblioteca react-router:

    bun add react-router
    bun add -d @types/react-router

    Ruta

    Imagina que tens un component Student (modifica el fitxer App.tsx).

    Ruta

    export default function App() {
    
      return <Student/>
    }
    
    function Student() {
      return <h1>Student</h1>
    }

    Inicia Vite:

    bun run dev

    Si obres el navegador al path / http://localhost:5173/ es renderitzarà el component Student.

    El curiós és que si obres el navegador al path /hello http://localhost:5173/student també es renderitzarà el component Student

    El que faràs a continuació és que el component App en lloc de retornar el component Student retorni el component BrowserRouter.

    import {BrowserRouter, Routes} from "react-router"
    
    export default function App() {
    
      return (
        <BrowserRouter>
          <Routes>
          </Routes>
        </BrowserRouter>)
    }

    Com no has definit cap ruta, el component BrowserRouter retorna un contingut buit pel path /: </>

    Afegeix aquesta ruta a Routes: / -> <Student/>:

    import {BrowserRouter, Routes, Route} from "react-router"
    
    export default function App() {
    
      return (
        <BrowserRouter>
          <Routes>
            <Route path={"/"} element={<Student/>}/>
          </Routes>
        </BrowserRouter>)
    }

    Ara, com el component BrowserRouter té definida una ruta pel path / que indica que ha de retornar el component Student, sí que es renderitza el component Student per a l’URL http://localhost:5173.

    I a més, el component Student es renderitza únicament per aquest path a diferència del que passava al principi.

    Modifica la ruta perquè el component Student es renderitzi al path /student.

    Verifica que funciona: http://localhost:5173/student

    Com pots intuir, dins del component Routes pots posar més components Route que apuntin al mateix o a altres components.

    Crea un component Home, i afegeix una ruta de / a <Home/>:

    Segment dinàmic

    La majoria dels components utilitzen props per crear el seu contingut.

    Modifica el component Student perquè utilitzi la propietat name

    function Student({name}: { name: string }) {
      return <h1>Student: {name}</h1>
    }

    Ara la ruta /student donarà error perquè el component Student requereix com a paràmetre la propietat name.

    Cap problema 🐱!

             // ...
            <Route path={"/student"} element={<Student name={"Laura"}/>}/>

    Verifica que la ruta torna a funcionar: http://localhost:5173/student

    El problema és que aquesta ruta només serveix per a Laura 👩‍🦰

    El path d’una URL està format per segments que es divideixen amb /, i si un segment comença amb : es converteix en un “segment dinàmic”.

    Per exemple, a /student/:name, /:name és un segment dinàmic.

    S’anomena dinàmic perquè admet qualsevol valor i es proporciona al component com un paràmetre.

    Modifica la ruta i elimina la prop name del component Student 😯

             // ...
            <Route path={"/student/:name"} element={<Student/>}/>
    
    // ...
    
    function Student() {
      return (<h1>Student: Laura 👩‍🦰</h1>)
    }

    Verifica que el router funciona per a qualsevol path /student/:name com [http://localhost:5173/laura](http://localhost:5173/laura, http://localhost:5173/eva, etc…

    … i que sempre renderitza el mateix estudiant “Laura 👩‍🦰”

    Llavors, resulta que ara:

    • Tens configurat el component BrowserRouter amb un component Route .
    • Perquè retorni el component Student quan el path del navegador coincideixi amb /student/:name.
    • On :name pot ser el que l’usuari vulgui.

    L’únic problema que has de resoldre és com pot el component Student accedir al paràmetre name?

    Per això està la funció useParams():

    function Student() {
      const {name} = useParams()
      return (<h1>Student: {name}</h1>)
    }

    I ara si vas a http://localhost:5173/eva la pàgina renderitza l’estudiant “eva 👩‍🦰”

    Crea el fitxer data.ts amb alguns estudiants:

    export type Student = {
        id: number
        emoji: string
        name: string
        surname: string
    }
    
    export const students: Student[] = [
        {id: 1, emoji: "👩‍🦰", name: "Mary", surname: "Sinclair"},
        {id: 2, emoji: "🧙‍♂️", name: "John", surname: "Smith"},
        {id: 3, emoji: "🐱", name: "Olivia", surname: "Hart"}
    ]

    Modifica la ruta de /student/:name a /student/:id per utilitzar l’id d’estudiant en lloc del name:

    Modifica el component Student perquè busqui l’estudiant a la llista students de data.tsx:

    Verifica que ara aquesta url http://localhost:5173/student/1 respon amb ”👩‍🦰 Mary Sinclair”

    Afegeix una nova ruta:

    export default function App() {
    
      return (
        <BrowserRouter>
          <Routes>
            <Route path={"/"} element={<Home/>}/>
            <Route path={"/student/"} element={<Students/>}/>
            <Route path={"/student/:id"} element={<Student/>}/>
          </Routes>
        </BrowserRouter>)
    }

    Crea el component Students que retorni una taula amb tots els estudiants:

    IDEmojiSurnameName
    1👩‍🦰SinclairMary
    2🧙‍♂️SmithJohn
    3🐱HartOlivia

    Una aplicació ha de tenir enllaços perquè l’usuari pugui navegar d’una pàgina a una altra pàgina.

    Modifica el component Home i afegeix un enllaç normal a /students:

    function Home() {
      return <a href="/student/">Students</a></li>
    }

    Però a l’exemple li falta una mica d’estil 🧙‍♂️ 💫

    function Home() {
    
      return (
    
        <div className={"container m-5"}>
    
          <ul className="nav nav-underline mb-3 justify-content-center">
            <li className="nav-item">
              <a className="nav-link active" aria-current="page" href={"/"}>Home</a>
            </li>
            <li className="nav-item">
              <a className="nav-link active" href={"/student/"}>Students</a>
            </li>
          </ul>
    
          <h3>Patufet School</h3>
    
        </div>
      )

    Quan l’usuari fa clic a l’enllaç, el servidor respon retornant una altra vegada tota l’aplicació, i com el path ha canviat BrowserRouter retorna el component corresponent.

    De funcionar funciona, funciona bé de moment, però no és gens eficient 🤔!

    React Router proporciona el component Link, que és un “wrapper” d’<a>, que el que fa és impedir que el navegador executi l’enllaç, i en el seu lloc li diu al BrowserRouter que actualitzi el contingut a la nova ruta.

    D’aquesta manera, tot i que sembli que estem navegant de pàgina a pàgina, en realitat estem navegant de component a component 😀!

    Modifica l’enllaç:

    function Home() {
      return (
        // ...
              <Link className="nav-link active" aria-current="page" to={"/"}>Home</Link>
    }

    Ves a l’inici (/), atura el servidor, i verifica que els enllaços funcionen!

    19:09:29 [vite] (client) hmr update /src/App.tsx (x22)
    error: script "dev" exited with code 58
    PS C:\Users\david\react-router>

    Aquest tipus de navegació s’anomena “client-site routing” perquè, tot i que el navegador mostri una url diferent, estàs navegant d’un component a un altre component dins de l’aplicació.

    Torna a arrancar el servidor:

    > bun run dev
    ...

    A continuació modifica el component <Students> de manera que cada estudiant de la taula tingui un enllaç a la ruta <Route path={"/student/:id"} element={<Student/>}/>.

    D’aquesta manera l’usuari pot fer clic i anar a la “pàgina” de l’estudiant:

    Crea un component Navbar amb una barra de navegació de Bootstrap - Navbar:

    Afegeix el component Navbar als components Home, Student i Students.

    Página web

    Com has vist, React et permet construir aplicacions client que no necessiten un servidor.

    Obre el terminal i executa:

    > bun run build
    
    vite v6.0.3 building for production...
    
     26 modules transformed.
    dist/index.html                  0.48 kB gzip:  0.32 kB
    dist/assets/index-COkpbCJz.js  145.81 kB gzip: 47.24 kB
     built in 707ms

    Al directori dist hi ha tots els fitxers necessaris per executar l’aplicació.

    A continuació pujaràs el contingut a un allotjament web:

    • Registra’t a Netlifly.
    • Crea un “new team” amb un pla gratuït:

    Puja el directori dist que acabes de crear:

    Ves a la pàgina del lloc (i canvia el nom si vols):

    Hi apareix la URL del lloc accessible des de qualsevol navegador connectat a Internet!

    Activitats

    Teachers

    Modifica el fitxer data.ts i afegeix uns quants professors a l’escola:

    En el aparece la URL del sitio accesible desde cualquier navegador conectado a Internet!

    Actividades

    Teachers

    Modifica el fichero data.ts y añade unos cuantos profesores a la escuela:

    export type Teacher = {
        id: string,
        name: string,
        avatar: string
    }
    
    export const teachers: Teacher[] = [
        {id: "pmoretti", name: "Pau Moretti", avatar: "https://i.pravatar.cc/100?img=11"},
        {id: "lsoler", name: "Laia Soler", avatar: "https://i.pravatar.cc/100?img=5"},
        {id: "msimon", name: "Marc Simon", avatar: "https://i.pravatar.cc/100?img=8"},
    ]

    Crea el fichero teachers.tsx con los componentes Teacher y Teachers:

    Añade las rutas /teacher/ y /teacher/:id al componete BrowserRouter.

    Sites

    Añade una ruta “Not Found”

    export default function App() {
    
    return (
    
    <BrowserRouter>
    <Routes>
        <Route path="*" element={<NotFound/>}/>
        <Route path="/" element={<Home/>}/>
        // ..
        }
    
        function NotFound() {
            return (
                <>
                      <p className="text-danger fs-4">404 Not Found</p>
                </>
        )
    }

    Añade alguna mejoras, por ejemplo:

    • Una pàgina con un mapa de donde está la escuela.
    • etc.

    Despliega la nueva version en Netlify.