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

  1. Quin problema resol un enrutador
  2. Per què React no porta enrutament
  3. Com funciona una aplicació d'una sola pàgina
  4. L'API d'historial del navegador: pushState i popstate
  5. Què hi guanya l'aplicació amb URLs reals
  6. Què és React Router i el seu lloc en l'ecosistema
  7. Les tres formes d'usar React Router v7
  8. Vocabulari del mòdul
  9. Historial de navegador enfront d'historial de hash
  10. El mapa de rutes de CicloUrbano

  1. 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-002 continua tenint https://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'useState es 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?), aquest useState es 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.

  1. 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.

  1. 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.

  1. L'API d'historial del navegador: pushState i popstate

React 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:

  • pushState canvia 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.
  • popstate només es dispara amb enrere/endavant, no amb pushState. Per això un enrutador escrit a mà necessita interceptar els clics a més de escoltar popstate.
  • L'estat que passes a pushState es serialitza i sobreviu a recàrregues dins de la mateixa sessió. React Router ho aprofita per a l'opció state de 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à.

  1. 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-003 i 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-02 torna 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.

  1. 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-02 surt de l'existència d'un fitxer app/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.

  1. 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. errorElement per gestionar errors per branca de ruta (06-03), useBlocker per impedir sortir d'un formulari a mitges i ScrollRestoration (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 handle i useMatches.
  • Separa la configuració de la interfície. El mapa viu a src/rutes.jsx i 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.

  1. 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).

  1. 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 : 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 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.

  1. 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-001bici-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 embolcalla App. Així Capcalera i PeuDePagina no 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. /taller continua 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 component». Decideix el mode en començar el projecte i no el barregis. En aquest curs: mode de dades, sempre.

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:

const [seccio, setSeccio] = useState(window.location.hash.slice(1) || 'cataleg');

Respon raonadament:

  1. Quin problema de la llista de l'apartat 1 resol aquest canvi?
  2. Quins continuen sense resoldre's?
  3. 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:

  1. Veure la fitxa de la bicicleta bici-005.
  2. Veure només les bicicletes de tipus carga al catàleg.
  3. Veure les incidències de l'estació est-02, dins de la seva pantalla de detall.
  4. Mostrar «Pàgina no trobada» quan algú escriu /estacionez.
  5. 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è:

  1. CicloUrbano tal com el construiràs: SPA amb Vite, desplegada a Netlify.
  2. 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.
  3. Una aplicació existent en React Router v6 amb 40 rutes, a la qual cal afegir dues pantalles noves la setmana que ve.
  4. Una web pública nova de CicloUrbano, amb catàleg indexable per cercadors i renderitzat en servidor.

Solucions

Solució 1

  1. Resol la recàrrega i, en part, els marcadors: si l'usuari és a #estaciones i 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.
  2. 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.
  3. Com a mínim calen dues coses: un useEffect que subscrigui un gestor a window.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 per history.pushState en lloc de per setSeccio a 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

  1. CicloUrbano a Netlify: mode de dades amb createBrowserRouter. És una SPA nova sense necessitats de servidor, vol URLs netes i farà servir errorElement, useBlocker i ScrollRestoration. Netlify permet configurar la reescriptura a index.html amb un fitxer _redirects d'una línia.
  2. Carpeta oberta des del disc (file://): createHashRouter, sense discussió. No hi ha servidor que pugui reescriure res, i amb createBrowserRouter la primera recàrrega trencaria l'aplicació. El mode pot continuar sent el de dades: createHashRouter també ho és.
  3. 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 guanyar useBlocker no 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.
  4. 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

Mòdul 2: Components de React

Mòdul 3: Treballar amb Esdeveniments

Mòdul 4: Conceptes Avançats de Components

Mòdul 5: Hooks de React

Mòdul 6: Enrutament a React

Mòdul 7: Gestió de l'Estat

Mòdul 8: Optimització del Rendiment

Mòdul 9: Proves a React

Mòdul 10: Temes Avançats

Mòdul 11: Projecte: Construir una Aplicació Completa

© Copyright 2026. Tots els drets reservats