El mòdul 5 va acabar amb un diagnòstic incòmode: CicloUrbano funciona, però és una sola pantalla. La Capcalera que vas escriure al mòdul 2 porta des de llavors un menú «Catàleg · Estacions · Les meves reserves» amb tres enllaços que no porten enlloc —són <a href="#cataleg"> decoratius—, i aquest deute no es podia saldar perquè faltava la peça que relaciona la URL del navegador amb quin component es pinta. Aquesta peça no ve amb React: cal afegir-la. En aquesta lliçó entendràs què és l'enrutament al client, com funciona per dins una aplicació d'una sola pàgina, què hi guanya CicloUrbano quan cada pantalla té la seva pròpia adreça, i quina de les tres formes d'usar React Router v7 faràs servir a la resta del curs. Encara no n'escriuràs la configuració —això és la lliçó següent—; aquí construeixes el model mental i el vocabulari que et caldran per no perdre't-hi.
Contingut
- Quin problema resol un enrutador
- Per què React no porta enrutament
- Com funciona una aplicació d'una sola pàgina
- L'API d'historial del navegador:
pushStateipopstate - Què hi guanya l'aplicació amb URLs reals
- Què és React Router i el seu lloc en l'ecosistema
- Les tres formes d'usar React Router v7
- Vocabulari del mòdul
- Historial de navegador enfront d'historial de hash
- El mapa de rutes de CicloUrbano
- Quin problema resol un enrutador
Comencem per l'estat actual del projecte. Si avui volguessis mostrar tres seccions diferents a CicloUrbano sense enrutador, faries alguna cosa així:
// src/App.jsx — enrutament "artesanal", el que NO mantindràs
import { useState } from 'react';
import Disseny from './components/Disseny.jsx';
import PanellCataleg from './components/PanellCataleg.jsx';
import PanellEstacions from './components/PanellEstacions.jsx';
import PanellReserves from './components/PanellReserves.jsx';
function App() {
const [seccio, setSeccio] = useState('cataleg');
return (
<Disseny alCanviarSeccio={setSeccio}>
{seccio === 'cataleg' && <PanellCataleg />}
{seccio === 'estacions' && <PanellEstacions />}
{seccio === 'reserves' && <PanellReserves />}
</Disseny>
);
}
export default App;Funciona. I tanmateix és una mala solució, per raons que no tenen res a veure amb l'estil del codi:
- La URL no canvia mai. Un usuari que veu la fitxa de
bici-002continua teninthttps://ciclourbano.test/a la barra d'adreces. No pot enviar aquest enllaç a ningú. - El botó «enrere» surt de l'aplicació. El navegador no té ni idea que l'usuari ha «navegat» tres vegades; per a ell no ha passat res des que va carregar la pàgina. Prémer enrere el retorna a Google.
- Recarregar (F5) torna al principi. L'
useStatees reinicia i torna al catàleg, tant se val on fos. - No hi ha marcadors. Desar una pantalla a preferits desa sempre la portada.
- No hi ha historial intern. No pots anar «enrere» dins de la pròpia aplicació.
- I escala fatal. Amb nou pantalles i paràmetres (
quina bicicleta? quina estació? quina pestanya?), aquestuseStatees converteix en un objecte amb cinc camps i una cadena de condicionals impossible de llegir.
Un enrutador és la peça que manté sincronitzades dues coses: la URL que veu l'usuari i l'arbre de components que pinta React. En un sentit, en canviar la URL canvia el que es veu; en l'altre, en navegar dins de l'aplicació canvia la URL.
Aquesta doble direcció és la clau. Un enrutador no és «un switch més elegant»: és la decisió de convertir la URL en estat de l'aplicació, un estat que viu fora de React, que el navegador ja sap gestionar i que l'usuari pot editar, copiar i compartir.
- Per què React no porta enrutament
És una pregunta legítima: Angular porta el seu enrutador de sèrie, Vue en té un d'oficial. Per què React no?
La resposta és en el que React diu de si mateix des de la lliçó 01-01: és una biblioteca per construir interfícies d'usuari, no un marc de treball d'aplicacions. El seu àmbit acaba en «donat un estat, produeix un arbre d'elements i sincronitza'l amb el DOM». Tota la resta —enrutament, peticions HTTP, gestió d'estat global, internacionalització— queda deliberadament fora.
Aquesta decisió té conseqüències pràctiques:
| Conseqüència | Detall |
|---|---|
| React funciona fora del navegador | React Native (10-05) no té URLs. Un enrutador basat en window.history no hi tindria sentit. |
| Competeix l'ecosistema | React Router, TanStack Router i altres han evolucionat en paral·lel; les millors idees d'un acaben en l'altre. |
| Cada projecte tria | Una aplicació incrustada en un panell d'administració pot no necessitar URLs; una botiga les necessita per al SEO. |
| El cost és teu | Instal·lar, triar versió, aprendre una API que no és a la documentació de React. |
La contrapartida és real: cal triar, i la tria s'equivoca sovint perquè el codi antic que trobes per internet fa servir APIs de fa tres versions. Per això l'apartat 7 d'aquesta lliçó és tan important.
- Com funciona una aplicació d'una sola pàgina
Compara els dos models de navegació. En una web tradicional (multipàgina), cada clic en un enllaç és una petició completa al servidor:
sequenceDiagram
participant U as Usuari
participant N as Navegador
participant S as Servidor
U->>N: Clic a «Estacions»
N->>N: Descarta la pàgina actual (pantalla en blanc)
N->>S: GET /estaciones
S-->>N: estaciones.html
N->>S: GET estils.css, scripts.js, imatges…
S-->>N: recursos
N->>N: Analitza l'HTML i pinta la pàgina des de zero
N-->>U: Pantalla nova (es va perdre tot l'estat de JavaScript)
En una aplicació d'una sola pàgina (SPA, single-page application) el servidor lliura un únic index.html la primera vegada, i a partir d'aquí JavaScript se n'encarrega de tot:
sequenceDiagram
participant U as Usuari
participant R as React Router
participant React
participant H as history (navegador)
U->>R: Clic en un <Link to="/estaciones">
R->>R: preventDefault() — no deixa que el navegador navegui
R->>H: history.pushState(null, '', '/estaciones')
Note over H: La barra d'adreces canvia,<br/>NO hi ha petició al servidor
R->>R: Compara '/estaciones' amb el mapa de rutes
R->>React: Renderitza <PaginaEstacions />
React->>React: Reconciliació (01-05): substitueix només el que canvia
React-->>U: Pantalla nova, sense recarregar, estat intacte
Les diferències que importen:
| Aspecte | Multipàgina tradicional | Aplicació d'una sola pàgina |
|---|---|---|
| Peticions per navegació | HTML + tots els recursos | Cap (o només dades JSON) |
| Parpelleig entre pantalles | Sí, pantalla en blanc | No |
| Estat de JavaScript | Es perd a cada navegació | Es conserva |
| Primera càrrega | Ràpida (HTML ja muntat) | Més lenta (cal descarregar el paquet JS) |
| Qui decideix què es veu | El servidor, per la ruta demanada | L'enrutador, al client |
| SEO per defecte | Bo | Requereix cura (o SSR, 10-01) |
Fixa't en el detall que fa possible tot el truc: preventDefault(). Un <a href="/estaciones"> normal provoca que el navegador tiri l'aplicació sencera i demani la pàgina al servidor. Un <Link to="/estaciones"> de React Router renderitza un <a> de veritat —amb el seu href, perquè es pugui obrir en pestanya nova i perquè els cercadors el segueixin— però intercepta el clic i el converteix en un canvi d'estat intern. Aquesta distinció tornarà a 06-02 i és la causa de l'error més comú del mòdul.
- L'API d'historial del navegador:
pushState i popstate
pushState i popstateReact Router no inventa res màgic: es recolza en una API estàndard del navegador que existeix des d'HTML5. Val la pena veure-la en cru una vegada, perquè entendre-la resol gairebé tots els dubtes posteriors.
// Exemple didàctic en JavaScript pur (no forma part de CicloUrbano)
// 1. Canviar la URL SENSE recarregar i afegint una entrada a l'historial
history.pushState({ pantalla: 'estaciones' }, '', '/estaciones');
// La barra d'adreces ara diu /estaciones.
// El servidor NO se n'ha assabentat de res. El DOM tampoc ha canviat:
// pintar la pantalla nova és responsabilitat TEVA.
// 2. Canviar la URL SENSE afegir entrada (substitueix l'actual)
history.replaceState({ pantalla: 'acceso' }, '', '/acceso');
// Útil després d'un inici de sessió: no vols que «enrere» torni al formulari.
// 3. Assabentar-te que l'usuari ha premut enrere o endavant
window.addEventListener('popstate', (esdeveniment) => {
console.log('L\'usuari ha navegat a:', location.pathname);
console.log('Estat associat:', esdeveniment.state); // { pantalla: 'estaciones' }
pintarPantalla(location.pathname); // altra vegada, cosa teva
});Tres observacions que convé gravar-se:
pushStatecanvia la URL però no pinta res. El navegador no dispara cap recàrrega ni cap esdeveniment. Qui decideix què mostrar és el teu codi. Aquest «teu codi» és exactament el que React Router t'estalvia d'escriure.popstatenomés es dispara amb enrere/endavant, no ambpushState. Per això un enrutador escrit a mà necessita interceptar els clics a més de escoltarpopstate.- L'estat que passes a
pushStatees serialitza i sobreviu a recàrregues dins de la mateixa sessió. React Router ho aprofita per a l'opcióstatede la navegació, que veuràs a 06-04.
Si haguessis d'escriure el teu propi enrutador, necessitaries: interceptar clics, cridar pushState, escoltar popstate, comparar la ruta amb un conjunt de patrons, extreure els paràmetres dels segments dinàmics, ordenar les coincidències per especificitat, gestionar rutes imbricades, restaurar el desplaçament… És exactament la feina que React Router porta feta des del 2014, i la raó de no fer-ho a mà.
- Què hi guanya l'aplicació amb URLs reals
Val la pena enumerar els guanys concrets, perquè justifiquen la complexitat afegida:
- Enllaços compartibles. Un operari pot passar a un altre
https://ciclourbano.test/bicicletas/bici-003i tots dos veuen la mateixa fitxa. Sense URLs, l'única instrucció possible és «entra i busca la Càrrega Max». - Enrere i endavant funcionen. El botó enrere és, de lluny, el control més utilitzat de qualsevol navegador. Que faci sortir de l'aplicació és una fallada greu d'experiència d'usuari.
- Recarregar manté el lloc. F5 a
/estaciones/est-02torna a/estaciones/est-02. Sense URLs, torna a la portada. - Marcadors. L'usuari pot desar «Les meves reserves» a preferits.
- Indexació per cercadors. Cada pantalla és un document diferent amb la seva adreça, i això és condició necessària (encara que no suficient: cal que el contingut sigui a l'HTML, i aquí entra l'SSR de 10-01) perquè un cercador la mostri.
- Analítica útil. Les eines de mesura compten pàgines vistes per URL. Amb una sola URL, totes les teves dades diuen «portada».
- Estat depurable. Quan un company et diu «em falla», la URL és la meitat de l'informe d'error.
- Divisió de codi per pantalla. Si
/tallerés una ruta pròpia, el seu JavaScript pot descarregar-se només quan algú hi entra. Això és la càrrega mandrosa que veuràs a 08-04, i sense rutes no hi ha per on tallar.
- Què és React Router i el seu lloc en l'ecosistema
React Router és la biblioteca d'enrutament per a React més veterana i més usada. Tradueix la URL actual a un arbre de components segons un mapa de rutes que tu declares, i ofereix els components i hooks per navegar, llegir paràmetres i compondre pantalles imbricades.
La seva història explica bona part de la confusió que trobaràs:
| Versió | Any aproximat | Novetat principal |
|---|---|---|
| v3 i anteriors | 2015–2016 | Rutes com a configuració estàtica, browserHistory global |
| v4/v5 | 2017–2019 | «Tot són components»: <Switch>, coincidència per ordre |
| v6 | 2021 | <Routes>, millor coincidència (ja no importa l'ordre), rutes relatives, hooks |
| v6.4 | 2022 | createBrowserRouter, loader, action: el mode «de dades» |
| v7 | 2024–2025 | Unificació amb Remix; tres modes oficials; paquet react-router |
I no està sol. Convé saber què hi ha al voltant, encara que en aquest curs facis servir React Router:
- TanStack Router. Alternativa moderna l'argument forta de la qual és la seguretat de tipus: rutes, paràmetres i paràmetres de consulta tipats d'extrem a extrem amb TypeScript (10-04), amb validació dels paràmetres inclosa. Si treballes en un projecte TypeScript gran i estricte, val la pena una ullada.
- Enrutadors integrats en els marcs de treball. Next.js (10-01 i 10-02) porta el seu enrutador basat en el sistema de fitxers: la ruta
/estaciones/est-02surt de l'existència d'un fitxerapp/estaciones/[estacionId]/page.jsx, sense que declaris cap mapa. El mateix fa Remix (avui fusionat amb React Router v7) i Astro. Quan adoptes un d'aquests marcs, no instal·les React Router: fas servir el seu. - Enrutadors minimalistes com
wouter, d'uns pocs kilobytes, per a aplicacions petites on React Router resulta excessiu.
La conclusió pràctica: React Router és l'opció per defecte assenyada per a una SPA construïda amb Vite, que és exactament el cas de CicloUrbano, i els conceptes que hi aprenguis (segments dinàmics, imbricació, outlet, rutes índex) són els mateixos en tots els enrutadors de l'ecosistema, amb altres noms.
- Les tres formes d'usar React Router v7
Aquí hi ha la principal font de desconcert per a qui comença avui. React Router v7 es pot fer servir de tres maneres diferents, i la documentació les anomena modes:
| Mode | Com es defineix el mapa | Què habilita | Quan triar-lo |
|---|---|---|---|
| Declaratiu | <BrowserRouter> + <Routes> + <Route> al JSX |
Enllaços, paràmetres, imbricació. Sense loader/action |
Migracions des de v6; aplicacions petites; afegir rutes a una aplicació ja existent |
| De dades | createBrowserRouter([...]) com a objecte + <RouterProvider> |
Tot l'anterior més loader, action, errorElement, useNavigation, useBlocker, ScrollRestoration |
SPA amb Vite que vol les capacitats modernes. És el d'aquest curs |
| De framework | Fitxer routes.ts + complement de Vite de React Router |
Tot el del mode de dades més renderitzat en servidor, generació de tipus, divisió de codi automàtica | Quan React Router fa de marc de treball complet, com a alternativa a Next.js |
En aquest curs fem servir el mode de dades: createBrowserRouter + RouterProvider. Les raons:
- És el que recomana la documentació oficial per a una SPA nova, i el camí de migració natural cap al mode de framework si algun dia cal.
- Desbloqueja APIs que necessitem.
errorElementper gestionar errors per branca de ruta (06-03),useBlockerper impedir sortir d'un formulari a mitges iScrollRestoration(06-04) només existeixen en el mode de dades. Amb el mode declaratiu et quedaries sense elles. - El mapa de rutes és una dada, no marcatge. Un array d'objectes es pot recórrer, transformar i fer servir per generar el menú o les molles de pa automàticament. A 06-03 ho aprofites amb
handleiuseMatches. - Separa la configuració de la interfície. El mapa viu a
src/rutes.jsxi no es barreja amb la pantalla.
Molt important per llegir codi d'altri: la forma antiga continua sent vàlida i és la que veuràs en la immensa majoria de codi existent, tutorials i respostes de fòrums:
// Mode DECLARATIU (herència de v6). Vàlid en v7, però NO és el que farem servir.
import { BrowserRouter, Routes, Route } from 'react-router';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<PaginaCataleg />} />
<Route path="/estaciones" element={<PaginaEstacions />} />
<Route path="*" element={<PaginaNoTrobada />} />
</Routes>
</BrowserRouter>
);
}I aquest és l'equivalent en el mode que sí que faràs servir, perquè vegis des d'ara el paregut:
// Mode DE DADES. Aquest és l'estil del curs (el muntaràs sencer a 06-02).
import { createBrowserRouter } from 'react-router';
export const router = createBrowserRouter([
{ path: '/', element: <PaginaCataleg /> },
{ path: '/estaciones', element: <PaginaEstacions /> },
{ path: '*', element: <PaginaNoTrobada /> }
]);Els conceptes es traslladen un a un. path, element, la imbricació, <Link>, useParams, useNavigate, <Outlet />: són idèntics en tots dos modes. L'única cosa que canvia és on declares el mapa i com es munta a l'arrel. Si demà caus en un projecte v6, el sabràs llegir sense esforç. La regla és la que ja vas aplicar amb els CSS Modules a 02-05: tria una forma i sigues coherent en tot el projecte; barrejar <BrowserRouter> amb <RouterProvider> en la mateixa aplicació és un error garantit.
Una nota més sobre noms de paquet, perquè desconcerta tothom: a v6 s'instal·lava react-router-dom; a v7 el paquet és react-router i d'aquí surt tot. react-router-dom es continua publicant com a reexportació per compatibilitat, així que veuràs totes dues importacions per la xarxa. Ho detallem a 06-02, on l'instal·laràs.
- Vocabulari del mòdul
Fixa aquests termes ara; la resta del mòdul els fa servir sense tornar-los a definir.
| Terme | Significat | Exemple a CicloUrbano |
|---|---|---|
| Ruta (route) | Regla que associa un patró d'URL amb un component | { path: '/estaciones', element: <PaginaEstacions /> } |
| Segment | Cada tros entre barres de la ruta | A /estaciones/est-02 n'hi ha dos: estaciones i est-02 |
| Segment dinàmic | Segment variable, escrit amb dos punts, el valor del qual es captura | /bicicletas/:bicicletaId captura bici-003 |
| Paràmetre de ruta (param) | El valor capturat per un segment dinàmic | { bicicletaId: 'bici-003' } via useParams() |
| Ruta imbricada | Ruta filla d'una altra, que es pinta dins del seu pare | incidencias dins de /estaciones/:estacionId |
outlet |
El forat on el pare pinta el seu fill actiu | <Outlet /> dins de Disseny |
| Ruta índex | Filla que es pinta quan la URL coincideix exactament amb el pare | La pestanya flota a /estaciones/est-02 |
| Ruta comodí (splat) | *, coincideix amb qualsevol cosa no capturada abans |
La pàgina de no trobat |
| Enllaç | <Link> o <NavLink>: navegació sense recarregar |
Els tres del menú de Capcalera |
| Navegació programàtica | Navegar des del codi, no des d'un clic en un enllaç | Anar a /reservas després de confirmar una reserva (06-04) |
| Paràmetres de consulta (query params) | El que va després de ?, per a filtres i ordenacions |
/?tipo=electrica |
location |
Objecte amb la URL actual descomposta | { pathname, search, hash, state, key } |
| Coincidència (match) | Ruta (o cadena de rutes) que la URL activa ara mateix | useMatches() a les molles de pa |
Un detall de criteri que sovint s'oblida, i que governa una decisió de disseny a 06-02: un segment dinàmic identifica un recurs; un paràmetre de consulta modifica una vista. La fitxa d'una bicicleta concreta és /bicicletas/bici-003 (identitat); un catàleg filtrat per tipus elèctric és /?tipo=electrica (modificació). Si dubtes, pregunta't si sense aquesta dada la pantalla continua tenint sentit: sense bicicletaId no hi ha fitxa a mostrar (dinàmic); sense tipo el catàleg es veu sencer (consulta).
- Historial de navegador enfront d'historial de hash
React Router pot gestionar la URL de dues maneres, i la tria té una conseqüència al servidor que sorprèn en desplegar per primera vegada.
Historial de navegador (createBrowserRouter) |
Historial de hash (createHashRouter) |
|
|---|---|---|
| URL d'exemple | https://ciclourbano.test/estaciones/est-02 |
https://ciclourbano.test/#/estaciones/est-02 |
| API que fa servir | history.pushState |
El fragment # de la URL |
| Aspecte | Net, indistingible d'una web tradicional | Amb coixinet, delator |
| SEO | Correcte | Dolent: el fragment no arriba al servidor |
| Requisit de servidor | Sí: reescriptura a index.html |
No: es serveix amb allotjament estàtic pur |
El requisit del servidor és la clau. Imagina't que l'usuari és a /estaciones/est-02 i prem F5. Aquesta vegada el navegador sí demana /estaciones/est-02 al servidor de veritat, perquè una recàrrega no la controla JavaScript. Si el servidor té un fitxer per ruta, no en trobarà cap i retornarà un 404, encara que a l'aplicació la ruta funcionés perfectament en navegar amb enllaços.
La solució és configurar el servidor perquè qualsevol ruta desconeguda retorni index.html i deixi que l'enrutador del client la resolgui. És una línia de configuració en qualsevol allotjament modern (Netlify, Vercel, Nginx, Apache), i el servidor de desenvolupament de Vite ja ho fa per defecte, raó per la qual aquest problema no apareix fins al dia del desplegament. Ho tractarem amb detall a 11-05.
flowchart TD
F5["L'usuari recarrega /estaciones/est-02"] --> SRV{"Té el servidor<br/>aquest fitxer?"}
SRV -->|"Sense configurar"| E404["404 Not Found 💥<br/>l'aplicació ni tan sols arrenca"]
SRV -->|"Amb reescriptura a index.html"| OK["Retorna index.html<br/>→ React arrenca<br/>→ l'enrutador llegeix la URL<br/>→ pinta PaginaDetalleEstacion ✅"]
style E404 fill:#fecaca
style OK fill:#dcfce7
Quan fer servir createHashRouter, doncs:
- Publiques en un allotjament estàtic que no permet configurar reescriptures (el cas clàssic: GitHub Pages sense trucs).
- Lliures l'aplicació com a fitxer solt, un CD-ROM intern o un
file://. - L'incrustes en un sistema heretat el servidor del qual no controles.
En qualsevol altre cas, createBrowserRouter, que és el que fa servir CicloUrbano. L'API és idèntica excepte el nom de la funció, així que canviar d'un a l'altre és qüestió d'una línia.
- El mapa de rutes de CicloUrbano
Aquest és el destí del mòdul. L'implementaràs a 06-02 i 06-03, i el respectaràs durant la resta del curs:
| Ruta | Pantalla | Notes |
|---|---|---|
/ |
Catàleg de bicicletes | Accepta ?tipo= per al filtre del SelectorTipus |
/bicicletas/:bicicletaId |
Fitxa d'una bicicleta | Segment dinàmic: bici-001 … bici-005 |
/estaciones |
Llistat d'estacions | Les tres TargetaEstacio |
/estaciones/:estacionId |
Detall d'estació | Amb pestanyes imbricades |
/estaciones/:estacionId (índex) |
Pestanya flota | Ruta índex: es veu per defecte |
/estaciones/:estacionId/incidencias |
Pestanya incidencias | Ruta imbricada germana |
/reservas |
Les meves reserves | Llegeix del ProveidorReserves |
/reservas/nueva |
Formulari de reserva | Redirigeix a /reservas en confirmar (06-04) |
/acceso |
Inici de sessió fictici | Tria entre usr-01 i usr-02 |
/taller |
Panell de l'operari | Protegida: només rol operario (06-05) |
* |
No trobat | Qualsevol URL que no coincideixi |
I així queda l'arbre, ja amb la imbricació que construiràs a 06-03:
flowchart TD
RAIZ["/ · Disseny<br/>(Capcalera + Outlet + PeuDePagina)"]
RAIZ --> IDX["index · PaginaCataleg"]
RAIZ --> BICI["bicicletas/:bicicletaId<br/>PaginaFitxaBicicleta"]
RAIZ --> EST["estaciones<br/>PaginaEstacions"]
RAIZ --> DET["estaciones/:estacionId<br/>PaginaDetallEstacio"]
DET --> FLO["index · PestanyaFlota"]
DET --> INC["incidencias · PestanyaIncidencies"]
RAIZ --> RES["reservas<br/>PaginaReserves"]
RAIZ --> NUE["reservas/nueva<br/>PaginaNovaReserva"]
RAIZ --> ACC["acceso<br/>PaginaAcces"]
RAIZ --> PROT["(sense path) RutaProtegida 🔒"]
PROT --> TAL["taller · PaginaTaller"]
RAIZ --> NF["* · PaginaNoTrobada"]
style PROT fill:#fde68a
style TAL fill:#fde68a
Observa tres decisions que es justificaran al seu moment:
Dissenyés la ruta arrel, no un component que embolcallaApp. AixíCapcaleraiPeuDePaginano es desmunten ni es tornen a muntar en canviar de pantalla (06-03).- El catàleg és una ruta índex, no
path: '/'repetit. És la pantalla que es veu quan la URL coincideix amb el pare i res més. - La branca protegida és una ruta sense
path: agrupa pantalles sota un guardià sense afegir cap segment a la URL./tallercontinua sent/taller, no/protegido/taller(06-05).
Errors Comuns i Consells
Creure que React Router fa peticions al servidor. No les fa. Canviar de ruta amb <Link> és una operació purament de client: s'actualitza la URL i React repinta. Les dades continuen venint d'on vinguessin (el teu domini.js, un fetch, un loader). Si en passar de / a /estaciones esperes que el servidor t'enviï HTML nou, tens el model mental equivocat.
Confondre el mode declaratiu amb el de dades i barrejar-ne les APIs. És l'error número u avui dia. Copies un exemple amb <BrowserRouter>, un altre amb createBrowserRouter, i acabes amb useNavigate llançant «useNavigate() may be used only in the context of a
Buscar a la documentació de React el que és a la de React Router. No hi ha cap menció a <Link> a react.dev, i no és un oblit: són projectes diferents, amb versions i calendaris diferents. Tingues a mà totes dues referències.
Instal·lar react-router-dom a v7 per costum. Funciona per compatibilitat, però el paquet correcte és react-router. Si barreges les dues importacions al mateix projecte pots acabar amb dues còpies de l'enrutador i errors de context incomprensibles.
Ficar a la URL coses que no hi han d'anar. La URL és pública, es comparteix i es desa a l'historial. Un filtre de catàleg o el número de pàgina sí que hi van; un testimoni de sessió, un correu electrònic o el contingut d'un formulari a mitges, no.
Consell: pensa el mapa de rutes abans d'escriure'l. Dibuixar en paper les pantalles i les seves adreces, com la taula de l'apartat 10, estalvia reestructuracions cares. La URL és una interfície pública de la teva aplicació: si demà canvies /bicis/:id per /bicicletas/:bicicletaId, trenques els enllaços que la gent ja ha desat.
Consell: fes servir noms de paràmetre descriptius. :bicicletaId i :estacionId en lloc de :id i :id. Quan a 06-03 llegeixis els paràmetres d'una ruta imbricada en tindràs diversos alhora, i dos id diferents són un problema; a més el codi es llegeix sol.
Exercicis
Exercici 1: diagnosticar l'enrutament artesanal
Repassa el component App de l'apartat 1, el que fa servir useState per triar secció. Un company proposa «millorar-lo» llegint el hash de la URL en arrencar:
Respon raonadament:
- Quin problema de la llista de l'apartat 1 resol aquest canvi?
- Quins continuen sense resoldre's?
- Què li falta com a mínim perquè el botó «enrere» funcioni?
Exercici 2: classificar el mapa de rutes
Per a cadascuna d'aquestes necessitats de CicloUrbano, decideix si correspon a un segment dinàmic, un paràmetre de consulta, una ruta imbricada o una ruta comodí, i escriu la URL resultant:
- Veure la fitxa de la bicicleta
bici-005. - Veure només les bicicletes de tipus
cargaal catàleg. - Veure les incidències de l'estació
est-02, dins de la seva pantalla de detall. - Mostrar «Pàgina no trobada» quan algú escriu
/estacionez. - Ordenar el llistat d'estacions per nombre de places, de més a menys.
Exercici 3: triar mode i tipus d'historial
Per a cada escenari, digues quin mode de React Router v7 (declaratiu, de dades, de framework) i quin creador d'enrutador (createBrowserRouter o createHashRouter) faries servir, i per què:
- CicloUrbano tal com el construiràs: SPA amb Vite, desplegada a Netlify.
- Un panell intern que es distribueix com una carpeta de fitxers que els operaris obren des del disc de xarxa, amb doble clic sobre
index.html. - Una aplicació existent en React Router v6 amb 40 rutes, a la qual cal afegir dues pantalles noves la setmana que ve.
- Una web pública nova de CicloUrbano, amb catàleg indexable per cercadors i renderitzat en servidor.
Solucions
Solució 1
- Resol la recàrrega i, en part, els marcadors: si l'usuari és a
#estacionesi prem F5, l'aplicació arrenca a la secció correcta, perquè el hash sí que es conserva i l'estat inicial es calcula a partir d'ell. També podria desar la URL a preferits. - Continuen sense resoldre's: el botó enrere (ningú escolta
popstate, així que en retrocedir canvia el hash però React no se n'assabenta i la pantalla no canvia); els enllaços compartibles amb paràmetres (quina bicicleta?no hi cap en un sol hash sense inventar-se un format); el creixement del condicional; i la indexació per cercadors, perquè el hash no arriba al servidor. - Com a mínim calen dues coses: un
useEffectque subscrigui un gestor awindow.addEventListener('popstate', …)—amb la seva neteja corresponent, com a 05-02— per sincronitzar l'estat quan l'usuari navega cap enrere, i que tots els canvis de secció passin perhistory.pushStateen lloc de persetSeccioa seques. Tan bon punt escriguis aquestes dues peces hauràs començat a construir el teu propi enrutador, que és justament el que convé no fer.
Solució 2
| Necessitat | Tipus | URL |
|---|---|---|
1. Fitxa de bici-005 |
Segment dinàmic | /bicicletas/bici-005 |
| 2. Catàleg filtrat per càrrega | Paràmetre de consulta | /?tipo=carga |
3. Incidències de est-02 |
Ruta imbricada (amb segment dinàmic al pare) | /estaciones/est-02/incidencias |
| 4. URL inexistent | Ruta comodí | /estacionez → coincideix amb * |
| 5. Estacions ordenades per places | Paràmetre de consulta | /estaciones?orden=plazas&sentido=desc |
El criteri en els casos 2 i 5 és el de l'apartat 8: el filtre i l'ordenació modifiquen una vista que existeix igual sense ells, així que van a la consulta. En el cas 1, sense bici-005 no hi ha fitxa possible: és identitat, i va al camí.
Solució 3
- CicloUrbano a Netlify: mode de dades amb
createBrowserRouter. És una SPA nova sense necessitats de servidor, vol URLs netes i farà servirerrorElement,useBlockeriScrollRestoration. Netlify permet configurar la reescriptura aindex.htmlamb un fitxer_redirectsd'una línia. - Carpeta oberta des del disc (
file://):createHashRouter, sense discussió. No hi ha servidor que pugui reescriure res, i ambcreateBrowserRouterla primera recàrrega trencaria l'aplicació. El mode pot continuar sent el de dades:createHashRoutertambé ho és. - Aplicació v6 amb 40 rutes: mantenir el mode declaratiu que ja té i afegir les dues pantalles noves com dos
<Route>més. Reescriure 40 rutes per guanyaruseBlockerno compensa; si l'equip decideix migrar, que sigui un projecte propi i no un efecte col·lateral d'un lliurament. Coherència per sobre de modernitat. - Web pública amb SEO i renderitzat en servidor: el mode de framework de React Router v7 o directament Next.js (10-01). Tan bon punt necessites HTML generat al servidor, l'enrutament deixa de ser només client i el marc de treball s'ha d'encarregar de tots dos costats. Aquí ni tan sols tries entre historial de navegador i de hash: el hash queda descartat perquè mai arriba al servidor.
Conclusió
L'enrutament al client resol el problema amb què es va tancar el mòdul 5: fer que la URL i la interfície es corresponguin. Has vist que una aplicació d'una sola pàgina lliura un únic index.html i que, a partir d'aquí, la navegació consisteix a interceptar els clics, cridar history.pushState per canviar l'adreça sense recarregar i deixar que l'enrutador decideixi quin component pintar, amb la reconciliació de 01-05 encarregant-se de tocar només el que canvia. A canvi d'aquesta complexitat, CicloUrbano guanya enllaços compartibles, botons d'enrere i endavant que funcionen, recàrrega al punt correcte, marcadors, analítica per pantalla, indexació i la possibilitat de carregar el codi de cada secció només quan cal.
També has fixat les decisions que governen la resta del mòdul. React no porta enrutador perquè és una biblioteca d'interfícies, no un marc de treball, i l'ecosistema ofereix React Router, TanStack Router i els enrutadors integrats de Next.js o Remix. De les tres formes d'usar React Router v7 —declarativa, de dades i de framework— el curs fa servir la de dades, amb createBrowserRouter i RouterProvider, perquè és la recomanada per a una SPA amb Vite i l'única que habilita errorElement, useBlocker i ScrollRestoration; el clàssic <BrowserRouter><Routes><Route> de v6 continua sent vàlid, és el que veuràs en moltíssim codi existent, i tots els conceptes es traslladen tal qual. Tens el vocabulari del mòdul —ruta, segment dinàmic, ruta imbricada, outlet, ruta índex, enllaç, navegació programàtica, paràmetres de consulta—, saps per què createBrowserRouter exigeix que el servidor reescrigui a index.html i en quins casos rars cal createHashRouter, i tens al davant el mapa complet de rutes de CicloUrbano.
Toca construir-lo. A la propera lliçó instal·laràs React Router, crearàs src/rutes.jsx amb el mapa, muntaràs <RouterProvider /> a main.jsx respectant l'ordre de proveïdors que ja existeix, reorganitzaràs el projecte amb una carpeta src/pagines/, substituiràs per fi els <a href="#…"> de la Capcalera per <NavLink> amb classe activa, llegiràs l'identificador de la bicicleta amb useParams i posaràs el filtre del SelectorTipus a la URL amb useSearchParams. La propera lliçó és Configuració de React Router.
Curs de React
Mòdul 1: Introducció a React
- Què és React?
- Configuració de l'Entorn de Desenvolupament
- Hola Món amb React
- JSX: Extensió de Sintaxi de JavaScript
- Com Renderitza React: Virtual DOM i Reconciliació
Mòdul 2: Components de React
- Entendre els Components
- Components Funcionals vs de Classe
- Props: Passar Dades als Components
- State: Gestió de l'Estat del Component
- Estils en els Components: CSS, Mòduls i Utilitats
Mòdul 3: Treballar amb Esdeveniments
- Gestió d'Esdeveniments a React
- Renderitzat Condicional
- Llistes i Claus
- Formularis i Components Controlats
- Validació de Formularis i Components No Controlats
- Accessibilitat en Components Interactius
Mòdul 4: Conceptes Avançats de Components
- Elevar l'Estat
- Composició vs Herència
- Mètodes del Cicle de Vida de React
- Hooks: Introducció i Ús Bàsic
- Límits d'Error: Capturar Fallades a la Interfície
Mòdul 5: Hooks de React
- Hook useState
- Hook useEffect
- Hook useRef i Accés al DOM
- Hook useContext
- Hook useReducer
- Hooks Personalitzats
Mòdul 6: Enrutament a React
- Introducció a React Router
- Configuració de React Router
- Rutes Imbricades
- Navegació Programàtica
- Rutes Protegides i Control d'Accés
Mòdul 7: Gestió de l'Estat
- Introducció a la Gestió de l'Estat
- API de Context
- Redux: Introducció i Configuració
- Redux: Accions i Reductors
- Redux: Connectar-lo a React
- Estat del Servidor: Peticions, Memòria Cau i Sincronització
Mòdul 8: Optimització del Rendiment
- Tècniques d'Optimització del Rendiment a React
- Memoïtzació amb React.memo
- Hooks useMemo i useCallback
- Divisió de Codi i Càrrega Mandrosa
- Mesurar el Rendiment amb React DevTools Profiler
Mòdul 9: Proves a React
- Introducció a les Proves
- Proves Unitàries amb Jest
- Proves de Components amb React Testing Library
- Proves de Codi Asíncron i Simulació d'APIs
- Proves d'Extrem a Extrem amb Cypress
Mòdul 10: Temes Avançats
- Renderitzat al Servidor (SSR) amb Next.js
- Generació de Llocs Estàtics (SSG) amb Next.js
- Suspense i React Server Components
- TypeScript amb React
- React Native: Creació d'Aplicacions Mòbils
