Router
React Router et permet renderitzar un component concret en funció del "path" de la url i navegar entre components.
Introducció
Section titled “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:
Entorn de treball
Section titled “Entorn de treball”Crea un projecte amb Bun
bun create vite router --template react-swc-tscd routerbun update
Instal·la la biblioteca react-router
:
bun add react-routerbun add -d @types/react-router
Imagina que tens un component Student
(modifica el fitxer App.tsx
).
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
// ...
export default function App() {
return (<BrowserRouter><Routes><Route path={"/student"} element={<Student/>}/></Routes></BrowserRouter>)}
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/>
:
// ...
export default function App() {
return (<BrowserRouter><Routes><Route path={"/"} element={<Home/>}/><Route path={"/student"} element={<Student/>}/></Routes></BrowserRouter>)}
function Home() {return <h1>Home</h1>}
Segment dinàmic
Section titled “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 componentRoute
. - 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
:
// ...<Route path={"/student/:id"} element={<Student/>}/>
Modifica el component Student
perquè busqui l’estudiant a la llista students
de data.tsx
:
import {students} from "./data.ts";
// ...
function Student() {
const {id} = useParams()
const student = students.find(student => student.id == Number(id))if (student == null) {return <p>Student not found!</p>}
return <p>{student.emoji} {student.name} {student.surname}</p>}
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:
ID | Emoji | Surname | Name |
---|---|---|---|
1 | 👩🦰 | Sinclair | Mary |
2 | 🧙♂️ | Smith | John |
3 | 🐱 | Hart | Olivia |
function Students() {
return (<div className="container m-5 w-50"><h3>Students</h3><table className={"table table-bordered mt-5"}><tr> <th>ID</th> <th>Emoji</th> <th>Surname</th> <th>Name</th></tr>{students.map(student => <tr> <td>{student.id}</td> <td className={"fs-4"}>{student.emoji}</td> <td>{student.surname}</td> <td>{student.name}</td></tr>)}</table></div>)}
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 58PS 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:
// ...{students.map(student => <tr> <td> <Link to={"/student/" + student.id} className={"btn btn-primary btn-large"}>{student.id}</Link> </td> <td className={"fs-4"}>{student.emoji}</td> </tr>)}
Navbar
Section titled “Navbar”Crea un component Navbar
amb una barra de navegació de Bootstrap - Navbar:
import {NavLink} from "react-router";
export default function Navbar() { return ( <nav className="navbar navbar-expand-lg bg-body-tertiary"> <div className="container-fluid"> <NavLink className="navbar-brand" to="/">Patufet</NavLink> // ... </div> </nav>)}
Afegeix el component Navbar
als components Home
, Student
i Students
.
function Home() {return <Navbar/>}
Página web
Section titled “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 kBdist/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
Section titled “Activitats”Teachers
Section titled “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
Section titled “Actividades”Teachers
Section titled “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
:
export function Teachers() { return ( <div className="container py-4"> <h2 className="text-center mb-4">Teachers</h2> // ... </div> ) }
// ...
Añade las rutas /teacher/
y /teacher/:id
al componete BrowserRouter
.
export default function App() { return ( <BrowserRouter> <Menu /> <Routes> <Route path="/" element={<Home />} /> <Route path="/student/" element={<Students/>}/> <Route path="/student/:id" element={<Student/>}/> <Route path="/teacher/" element={<Teachers/>}/> <Route path="/teacher/:id" element={<Teachers/>}/> </Routes> </BrowserRouter> )}
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.
El contingut d'aquest lloc web té llicència CC BY-NC-ND 4.0.
©2022-2025 xtec.dev