Portes usant un hook des de la lliçó 02-04 sense haver-li posat nom a la categoria: useState. I a les dues últimes lliçons han aparegut les raons històriques per les quals els hooks existeixen: l'infern d'embolcalls dels HOC i les render props (04-02), i la lògica relacionada repartida entre mètodes del cicle de vida que estaven a cinquanta línies els uns dels altres (04-03). Aquesta lliçó tanca aquest cercle. Entendràs què és exactament un hook, quin problema va venir a resoldre, per què té dues regles d'ús aparentment arbitràries —i el mecanisme intern que les explica—, quins hooks existeixen i en quina lliçó del curs es veu cadascun. No en desenvoluparem cap a fons: aquesta és la feina del Mòdul 5 complet. Aquí muntem el mapa abans de recórrer el territori.
Contingut
- Quin problema van venir a resoldre els hooks
- Què és un hook, amb precisió
- Regla 1: només al nivell superior
- El mecanisme: la llista ordenada de hooks
- Regla 2: només des de components o des d'altres hooks
eslint-plugin-react-hooks, la xarxa de seguretat- Catàleg dels hooks del curs
- Ús bàsic combinat:
PanellResumambuseState+useEffect - Estat local, referència i context: tres coses diferents
- Quin problema van venir a resoldre els hooks
Abans de 2019, un component de React havia de triar entre dues formes incompatibles:
- Component de funció: senzill, llegible, sense
this… però sense estat i sense accés al cicle de vida. Només servia per a presentació. - Component de classe: ho podia tot, a canvi de constructor,
bind,thisi mètodes de cicle de vida.
Això obligava a un refactor complet així que un component de presentació necessitava recordar una dada. Però el problema de fons era un altre, i més greu: no hi havia cap manera decent de reutilitzar lògica amb estat.
Suposa que tres components de CicloUrbano necessiten saber l'amplada de la finestra per decidir quantes targetes hi caben. Amb classes, la lògica —subscriure's a l'esdeveniment resize, guardar l'amplada, donar-se de baixa— calia copiar-la als tres, o embolicar els tres en un HOC, o ficar-los dins d'una render prop. Les dues últimes opcions produïen arbres com aquest:
// El que es veia a React DevTools amb quatre HOC apilats
<ambAutenticacio(ambTema(ambAmplada(ambRegistre(PanellOperari))))>
<ambTema(ambAmplada(ambRegistre(PanellOperari)))>
<ambAmplada(ambRegistre(PanellOperari))>
<ambRegistre(PanellOperari)>
<PanellOperari /> ← l'únic que pinta alguna cosaQuatre nivells que no dibuixen res, props que apareixen sense origen visible i una depuració incòmoda. Això és l'infern d'embolcalls (wrapper hell) que vas veure a 04-02.
El segon problema era l'organització per moments en lloc de per assumptes, que vam analitzar a 04-03:
componentDidMount() {
this.carregarBicicletes(); // assumpte A
window.addEventListener('resize', this.mesurarAmplada); // assumpte B
this.temporitzador = setInterval(this.actualitzarRellotge, 1000); // assumpte C
}
componentWillUnmount() {
window.removeEventListener('resize', this.mesurarAmplada); // assumpte B, aquí a sota
clearInterval(this.temporitzador); // assumpte C, aquí a sota
}Tres assumptes que no tenen res a veure comparteixen mètode, i cadascun té la seva altra meitat en un mètode diferent. És la recepta perfecta per oblidar-se d'una neteja.
Els hooks resolen les tres coses alhora:
| Problema anterior | Resposta dels hooks |
|---|---|
| Les funcions no podien tenir estat | useState i companyia funcionen a qualsevol component de funció |
| Reutilitzar lògica amb estat exigia embolcalls | Un hook personalitzat és una crida a funció, sense nivell extra a l'arbre |
| La lògica relacionada quedava escampada | Cada assumpte va al seu propi bloc, amb la seva neteja al costat |
El this i els bind |
Desapareixen: no hi ha classe |
I una cosa que no van fer: eliminar les classes. Segueixen funcionant, i per als límits d'error encara són obligatòries (04-05).
- Què és un hook, amb precisió
Un hook és una funció de JavaScript el nom de la qual comença per
usei que enganxa el component al sistema intern de React, permetent-li accedir a capacitats com l'estat, els efectes o el context.
Desglossem la definició:
- És una funció normal. No és una paraula clau del llenguatge, no és sintaxi especial, no requereix compilació.
useStateés una funció que React exporta i tu importes. - El seu nom comença per
use. No és decoratiu: és la convenció per la qual React i les eines d'anàlisi reconeixen un hook.eslint-plugin-react-hooksdecideix si una funció és un hook mirant el seu nom, i aplica les regles en conseqüència. Si escrius la teva pròpia funció amb estat i la anomenesobtenirAmpladaen lloc deuseAmplada, les regles no s'aplicaran i perdràs la xarxa de seguretat. - Enganxa amb el sistema intern. Aquí hi ha la clau conceptual. Un component de funció és només una funció: quan acaba, les seves variables desapareixen. Aleshores, on es guarda l'estat entre renders? Fora del component, en una estructura de dades interna de React, associada a aquesta instància concreta. El hook és el cable que connecta la funció amb aquest magatzem.
function ComptadorPlaces() {
const [places, setPlaces] = useState(20);
// `places` és una variable local que mor en acabar la funció.
// El VALOR 20 (i després 19, 18…) no viu aquí: viu dins de React.
// useState és el cable que va a buscar-lo a cada render.
return <p>Places lliures: {places}</p>;
}Aquesta idea —l'estat viu a React, no a la funció— és el que fa possibles les dues regles de l'apartat següent. I també explica una cosa que ja vas observar a 02-04: dues instàncies del mateix component tenen estats independents, perquè React guarda un magatzem per instància, no per component.
- Regla 1: només al nivell superior
Crida els hooks únicament al nivell superior del component. Mai dins de condicionals, bucles, funcions imbricades ni després d'un
returnanticipat.
// CORRECTE: els tres hooks, al nivell superior, sempre en el mateix ordre
function PanellReservaAvancat({ bicicleta }) {
const [hores, setHores] = useState(1);
const [confirmada, setConfirmada] = useState(false);
const referencia = useRef(null);
if (!bicicleta) {
return <p>Selecciona una bicicleta.</p>; // el return va DESPRÉS dels hooks
}
// …
}// INCORRECTE: el hook és dins d'un if
function PanellReservaAvancat({ bicicleta }) {
if (bicicleta) {
const [hores, setHores] = useState(1); // ❌
}
// …
}// INCORRECTE: return anticipat ABANS d'un hook
function PanellReservaAvancat({ bicicleta }) {
const [hores, setHores] = useState(1);
if (!bicicleta) return null; // ❌ el hook següent a vegades no s'executa
const [confirmada, setConfirmada] = useState(false);
// …
}// INCORRECTE: hook dins d'un bucle
function LlistaComptadors({ estacions }) {
return estacions.map((estacio) => {
const [places, setPlaces] = useState(estacio.places); // ❌
return <p key={estacio.id}>{places}</p>;
});
}L'últim cas té una solució conceptual, no tècnica: si cada estació necessita el seu propi estat, cada estació necessita el seu propi component. Extraus ComptadorPlaces i crides el hook dins d'ell. Cada instància tindrà el seu magatzem, i la regla es compleix sola.
Per què regles tan estrictes? Perquè el mecanisme intern de React depèn de l'ordre. Anem a veure-ho.
- El mecanisme: la llista ordenada de hooks
React no guarda l'estat pel nom de la variable. No pot: const [hores, setHores] és destructuring d'un array, i React mai veu el nom hores. El que fa és molt més simple: manté, per a cada instància de component, una llista ordenada de cel·les, i un punter que avança una posició amb cada crida a un hook.
flowchart LR
subgraph R1["Primer render"]
H1["useState(1)"] --> C1["cel·la 0<br/>valor: 1"]
H2["useState(false)"] --> C2["cel·la 1<br/>valor: false"]
H3["useRef(null)"] --> C3["cel·la 2<br/>valor: {current: null}"]
end
subgraph R2["Renders següents"]
S1["useState(1)"] --> D1["llegeix cel·la 0"]
S2["useState(false)"] --> D2["llegeix cel·la 1"]
S3["useRef(null)"] --> D3["llegeix cel·la 2"]
end
R1 --> R2
En el primer render React crea les cel·les en l'ordre en què es criden els hooks. En tots els renders següents es limita a llegir-les en el mateix ordre. El vincle entre useState(1) i el seu valor guardat és exclusivament la posició.
D'aquí surt la regla: si en un render es criden tres hooks i en el següent només dos, les posicions es desplacen i cada variable rep el valor d'una altra.
El desastre, pas a pas
// CODI TRENCAT, per entendre el mecanisme
function PanellReserva({ bicicleta }) {
const [hores, setHores] = useState(1); // cel·la 0
if (bicicleta) {
const [nota, setNota] = useState(''); // ❌ cel·la 1 només a vegades
}
const [confirmada, setConfirmada] = useState(false); // cel·la 1 o cel·la 2?
// …
}sequenceDiagram
participant R1 as Render 1 (bicicleta = objecte)
participant M as Cel·les de React
participant R2 as Render 2 (bicicleta = null)
R1->>M: useState(1) → cel·la 0 = 1
R1->>M: useState('') → cel·la 1 = ''
R1->>M: useState(false) → cel·la 2 = false
Note over M: [1, '', false]
R2->>M: useState(1) → llegeix cel·la 0 = 1 ✅
R2->>M: (l'if no es compleix: no hi ha crida)
R2->>M: useState(false) → llegeix cel·la 1 = '' ❌
Note over R2: `confirmada` val '' (una cadena)<br/>en lloc de false
El resultat en pantalla: confirmada passa a valer '' en lloc de false, i setConfirmada escriu a la cel·la que pertanyia a nota. No hi ha cap error de sintaxi, l'aplicació no s'atura i l'error es manifesta com un comportament absurd molt difícil de rastrejar. React detecta molts d'aquests casos i avisa amb:
Warning: React has detected a change in the order of Hooks called by PanelReserva.
This will lead to bugs and errors if not fixed.Quan vegis aquest missatge, busca un hook dins d'un if, d'un bucle, d'un try/catch o després d'un return anticipat. Sempre és això.
La conseqüència pràctica: el nombre i l'ordre de les crides a hooks ha de ser idèntic a tots els renders d'un component. Per això els hooks s'escriuen tots junts, a dalt de tot, abans de qualsevol lògica condicional. I si necessites un valor condicional, la condició va dins del hook, no al voltant:
// En lloc de posar el hook dins de l'if, posa l'if dins del hook
const [nota, setNota] = useState(bicicleta ? '' : 'sense bicicleta');
- Regla 2: només des de components o des d'altres hooks
Crida els hooks només des de components de funció de React o des d'altres hooks personalitzats. Mai des de funcions JavaScript normals.
// CORRECTE: des d'un component
function ResumFlota({ flota }) {
const [ordre, setOrdre] = useState('model');
// …
}
// CORRECTE: des d'un hook personalitzat (el nom comença per use)
function useFlotaOrdenada(flota) {
const [ordre, setOrdre] = useState('model');
return { ordre, setOrdre };
}
// INCORRECTE: des d'una funció normal
function calcularDisponibles(flota) {
const [comptador, setComptador] = useState(0); // ❌ de quin component és aquest estat?
return flota.filter((b) => b.estat === 'disponible').length;
}
// INCORRECTE: des d'un gestor d'esdeveniment
function gestionarClic() {
const [obert, setObert] = useState(false); // ❌ s'executa fora del render
}La raó surt directament de l'apartat anterior: les cel·les pertanyen a una instància de component que s'està renderitzant ara mateix. Si crides un hook des d'una funció solta, React no sap a quina llista de cel·les adreçar-se, i llança:
El cas del gestor d'esdeveniment mereix un avís, perquè és un error freqüent en qui comença: els gestors s'executen després del render, quan ja no hi ha cap renderització en curs. Els hooks es criden durant el render; els gestors usen el que aquests hooks han retornat.
// El patró correcte
function TargetaBicicleta({ bicicleta }) {
const [destacada, setDestacada] = useState(false); // hook: en el render
function gestionarClic() {
setDestacada(!destacada); // gestor: usa el resultat
}
return <article onClick={gestionarClic}>…</article>;
}I la conseqüència constructiva d'aquesta regla és la que dona sentit a tot: una funció que comença per use i crida hooks és un hook personalitzat, i això és tot el que cal per reutilitzar lògica amb estat. Sense embolcalls, sense nivells nous a l'arbre. El construiràs a Hooks Personalitzats.
eslint-plugin-react-hooks, la xarxa de seguretat
eslint-plugin-react-hooks, la xarxa de seguretatLes dues regles són fàcils de trencar sense adonar-se'n, sobretot en refactoritzar. Per això l'equip de React manté un plugin d'ESLint que les comprova automàticament, i que les plantilles de Vite per a React ja porten configurat.
// eslint.config.js (fragment)
import reactHooks from 'eslint-plugin-react-hooks';
export default [
{
plugins: { 'react-hooks': reactHooks },
rules: {
'react-hooks/rules-of-hooks': 'error', // les dues regles d'aquesta lliçó
'react-hooks/exhaustive-deps': 'warn' // dependències dels efectes (05-02)
}
}
];Què aporta cada regla:
| Regla | Què detecta | Severitat recomanada |
|---|---|---|
rules-of-hooks |
Hooks en condicionals, bucles, funcions normals o gestors | error: no hi ha falsos positius que justifiquin ignorar-la |
exhaustive-deps |
Dependències que falten o sobren a useEffect, useMemo i useCallback |
warn: gairebé sempre té raó, però hi ha casos legítims d'excepció |
La primera regla s'equivoca tan poc que convé tractar-la com un error de compilació. Si mai et ve de gust silenciar-la amb un comentari, la resposta correcta gairebé sempre és reestructurar el component —extreure un component fill, moure la condició dins del hook—, no callar l'avís.
- Catàleg dels hooks del curs
Aquest és el mapa complet. No memoritzis els detalls ara: la columna de la dreta et diu on s'explica cadascun a fons.
| Hook | Per a què serveix, en una frase | On s'estudia |
|---|---|---|
useState |
Guardar una dada que canvia i provoca un nou render en canviar | 05-01 |
useEffect |
Sincronitzar el component amb un sistema extern (temporitzadors, xarxa, subscripcions) | 05-02 |
useRef |
Guardar un valor sense provocar renders, i accedir a nodes del DOM | 05-03 |
useContext |
Llegir un valor compartit per tot un subarbre, sense perforació de props | 05-04 |
useReducer |
Gestionar estat complex amb moltes transicions, mitjançant accions i un reductor | 05-05 |
useMemo |
Recordar el resultat d'un càlcul car entre renders | 08-03 |
useCallback |
Recordar una funció entre renders per no trencar la memoïtzació dels fills | 08-03 |
useId |
Generar un identificador únic i estable per associar label amb camps |
05-03 (menció) i accessibilitat de 03-06 |
useTransition |
Marcar una actualització com a no urgent per no bloquejar la interfície | Mòdul 8 |
useDeferredValue |
Mostrar una versió «retardada» d'un valor mentre arriba el definitiu | Mòdul 8 |
use |
Llegir una promesa o un context directament durant el render (React 19) | Mòdul 10 |
| Hooks personalitzats | Empaquetar la teva pròpia lògica amb estat en una funció reutilitzable | 05-06 |
Dues observacions sobre el catàleg:
- La majoria dels components només necessiten
useState. Després, per freqüència, vanuseEffectiuseContext.useMemoiuseCallbacksón eines d'optimització que només s'apliquen després de mesurar un problema, i arriben al mòdul 8 precisament per això. - Hi ha més hooks dels que apareixen aquí (
useImperativeHandle,useSyncExternalStore,useDebugValue,useOptimistic,useActionState…). Són especialitzats i aquest curs els deixa fora tret de menció puntual. Amb la taula anterior cobreixes el noranta-cinc per cent del React que s'escriu cada dia.
- Ús bàsic combinat:
PanellResum amb useState + useEffect
PanellResum amb useState + useEffectUn aperitiu del mòdul 5. Aquest component de CicloUrbano combina els dos hooks que més usaràs: guarda un comptador de consultes del resum i sincronitza el títol de la pestanya del navegador amb la flota disponible.
// src/components/PanellResum.jsx
import { useState, useEffect } from 'react';
import estils from './PanellResum.module.css';
/**
* Resum consultable de la flota de CicloUrbano.
* Props:
* - flota (array de Bicicleta, obligatori)
*/
function PanellResum({ flota }) {
const [consultes, setConsultes] = useState(0);
const [missatge, setMissatge] = useState('Prem per actualitzar el resum.');
// Valors DERIVATS: es recalculen a cada render, no són estat (04-01)
const disponibles = flota.filter((bicicleta) => bicicleta.estat === 'disponible').length;
const enTaller = flota.filter((bicicleta) => bicicleta.estat === 'mantenimiento').length;
// EFECTE: sincronitza el títol del document amb el nombre de disponibles
useEffect(() => {
document.title = `CicloUrbano · ${disponibles} bicis disponibles`;
}, [disponibles]);
function gestionarConsulta() {
setConsultes((consultesPrevies) => consultesPrevies + 1);
setMissatge(
disponibles === 0
? 'No queda cap bicicleta disponible ara mateix.'
: `Hi ha ${disponibles} de ${flota.length} bicicletes llestes per llogar.`
);
}
return (
<section className={estils.panell}>
<h2>Resum de la flota</h2>
<p>Total: {flota.length} · Disponibles: {disponibles} · Al taller: {enTaller}</p>
<p aria-live="polite">{missatge}</p>
<p className={estils.comptador}>Consultes fetes: {consultes}</p>
<button type="button" onClick={gestionarConsulta}>
Actualitzar resum
</button>
</section>
);
}
export default PanellResum;Què fa cada peça i per què és on és:
- Els dos
useStatevan al principi, al nivell superior, un darrere l'altre. Ocupen les cel·les 0 i 1, en aquest ordre, a tots els renders. Regla 1 complerta. disponiblesienTallerno són estat. Es calculen deflotaa cada render. Si fossin estat, caldria recalcular-los a mà cada vegada que canvia la flota, amb el risc de desincronització que vas veure a 04-01.setConsultes((consultesPrevies) => …)usa la forma funcional perquè el valor nou depèn de l'anterior. És la precaució de 02-04.- El
useEffectsincronitza amb un sistema extern: eldocument.titlepertany al navegador, no a React. Aquí és on s'aplica el model mental de 04-03: no es tracta d'«executar codi en muntar», sinó de mantenir el títol sincronitzat ambdisponibles. Per aixòdisponiblesapareix a la llista de dependències[disponibles]: quan aquest número canviï, la sincronització es refarà. gestionarConsultano crida cap hook. Usa els resultats dels hooks (setConsultes,setMissatge,disponibles), que és exactament el repartiment correcte segons la regla 2.- L'
aria-live="polite"ve de 03-06: el missatge canvia sense que la persona usuària mogui el focus, i cal anunciar-ho.
I a App, sense cap cerimònia:
Res de HOC, res de render props, cap nivell extra a l'arbre. Dues crides a funció dins del component i ja té estat i efectes. Aquesta és l'aportació dels hooks resumida en un exemple.
- Estat local, referència i context: tres coses diferents
Per tancar el mapa, la distinció que més ordena les decisions que prendràs al mòdul 5:
| Eina | Guarda un valor que… | Provoca render en canviar | Abast | Es veu a |
|---|---|---|---|---|
Estat (useState) |
Es veu en pantalla i canvia amb el temps | Sí | La instància del component | 05-01 |
Referència (useRef) |
Cal recordar però no es pinta (un identificador de temporitzador, un node del DOM) | No | La instància del component | 05-03 |
Context (useContext) |
Necessiten molts components allunyats entre si (usuari, tema, idioma) | Sí, en qui el consumeix | Tot el subarbre sota el proveïdor | 05-04 |
En una frase cadascun:
- Estat: «el que es veu, i quan canvia cal repintar».
- Referència: «el que es recorda entre renders sense que ningú s'hagi de repintar». És exactament el
this.temporitzadorde la classePanellActivitatde 04-03. - Context: «el que està disponible per a tot un subarbre sense anar de mà en mà», la resposta a la perforació de props que va quedar pendent a 04-01.
Errors Comuns i Consells
- Hook dins d'un
if, un bucle o untry. Trenca la correspondència per posició i provoca errors incomprensibles. Si necessites condicionalitat, mou la condició dins del hook o extreu un component fill. - Hook després d'un
returnanticipat. És el mateix error disfressat: elreturnfa que les línies següents no s'executin en alguns renders. Tots els hooks van abans de qualsevolreturn. - Cridar un hook des d'un gestor d'esdeveniment. Els gestors s'executen fora del render. Crida el hook a dalt i usa el seu resultat dins del gestor.
- Anomenar
obtenirAlgoa una funció que crida hooks. Sense el prefixuse, el plugin d'ESLint no la reconeix com a hook i no comprova res dins d'ella. La convenció és funcional, no estètica. - Creure que
useStatedins d'un bucle dona «un estat per element». No el dona: dona un desastre. Un estat per element significa un component per element. - Silenciar
rules-of-hooksamb un comentari d'ESLint. És tractar el símptoma. La regla gairebé mai s'equivoca. - Consell: escriu tots els hooks junts, al principi del component. Estat, referències, context i efectes, en aquest ordre, i després la lògica derivada, els gestors i el
return. Si mantens aquesta disposició a tot el projecte, complir les regles és automàtic. - Consell: mira els hooks a React DevTools. En seleccionar un component de funció veuràs la llista dels seus hooks en ordre, amb els seus valors. És la llista de cel·les de l'apartat 4, feta visible.
Exercicis
Exercici 1. Aquest component de CicloUrbano trenca les regles dels hooks en tres llocs diferents. Identifica cada infracció, explica quina regla incompleix i quina conseqüència concreta té, i reescriu el component correctament.
function PanellEstacio({ estacio, mostrarDetall }) {
const [placesLliures, setPlacesLliures] = useState(estacio.places);
if (!estacio) {
return <p>Estació no trobada.</p>;
}
if (mostrarDetall) {
const [detallObert, setDetallObert] = useState(true);
}
function gestionarLloguer() {
const [ultimLloguer, setUltimLloguer] = useState(null);
setPlacesLliures(placesLliures + 1);
}
return (
<article>
<h3>{estacio.nom}</h3>
<p>Places lliures: {placesLliures}</p>
<button type="button" onClick={gestionarLloguer}>Retornar bicicleta</button>
</article>
);
}Exercici 2. Sense escriure codi, indica quin hook faries servir en cada situació de CicloUrbano i en quina lliçó s'estudia. Justifica breument cada elecció.
- Recordar quin tipus de bicicleta està filtrat al catàleg.
- Guardar l'identificador d'un
setTimeoutper poder cancel·lar-lo. - Que quinze components repartits per l'arbre coneguin l'usuari que ha iniciat sessió.
- Mantenir el
document.titleal dia amb el nombre de reserves actives. - Gestionar un formulari de reserva amb vuit camps i transicions complexes entre estats de validació.
Exercici 3. Amplia el PanellResum de l'apartat 8 amb un tercer tros d'estat, ultimaConsulta, que guardi l'hora de l'última consulta com una cadena llegible (toLocaleTimeString('ca-ES')), o null si encara no s'ha consultat. Mostra-la en pantalla només quan existeixi. Després respon: en quina posició de la llista de cel·les queda aquest nou estat i per què és important on el declaris?
Solucions
Solució 1. Les tres infraccions:
returnanticipat abans d'un hook (regla 1). Quanestacioés nul, la funció acaba abans d'arribar a les crides següents, i el nombre de hooks executats canvia entre renders. A més,useState(estacio.places)a la línia anterior ja hauria petat en llegir.placesdenull: la guarda arriba tard.useStatedins d'unif(regla 1).detallObertnomés es crea quanmostrarDetallés cert; en canviar aquesta prop, les cel·les es desplacen i els estats es barregen entre si.useStatedins d'un gestor d'esdeveniment (regla 2).gestionarLloguers'executa després del render, quan no hi ha renderització en curs: React llança «Invalid hook call».
I un quart problema, no de regles sinó de correcció: setPlacesLliures(placesLliures + 1) hauria d'usar la forma funcional, i a més no té límit superior (podria superar les places totals de l'estació).
function PanellEstacio({ estacio, mostrarDetall }) {
// 1. TOTS els hooks a dalt, sense condicions i abans de qualsevol return
const [placesLliures, setPlacesLliures] = useState(estacio ? estacio.places : 0);
const [detallObert, setDetallObert] = useState(true);
const [ultimLloguer, setUltimLloguer] = useState(null);
// 2. Els returns anticipats, DESPRÉS dels hooks
if (!estacio) {
return <p>Estació no trobada.</p>;
}
// 3. El gestor usa els resultats dels hooks; no en crida cap
function gestionarLloguer() {
setPlacesLliures((previes) => Math.min(estacio.places, previes + 1));
setUltimLloguer(new Date().toLocaleTimeString('ca-ES'));
}
return (
<article>
<h3>{estacio.nom}</h3>
<p>Places lliures: {placesLliures}</p>
{mostrarDetall && detallObert && <p>Barri: {estacio.barri}</p>}
{ultimLloguer && <p>Última devolució: {ultimLloguer}</p>}
<button type="button" onClick={gestionarLloguer}>Retornar bicicleta</button>
</article>
);
}Observa el patró general de la correcció: els hooks es declaren tots, sempre; el que és condicional és l'ús del seu valor al JSX, no la crida.
Solució 2.
| Cas | Hook | Lliçó | Per què |
|---|---|---|---|
| 1. Tipus filtrat al catàleg | useState |
05-01 | És una dada visible que canvia amb la interacció i ha de provocar un nou render. Viu a App des de 04-01 |
2. Identificador d'un setTimeout |
useRef |
05-03 | Cal recordar-lo entre renders però no es pinta; canviar-lo no ha de repintar res. És el this.temporitzador de 04-03 |
| 3. Usuari de la sessió, en quinze components | useContext |
05-04 | Elevar-lo a App i passar-lo per props provocaria la perforació de props anunciada a 04-01 |
4. document.title al dia |
useEffect |
05-02 | El títol del document és un sistema extern a React; cal sincronitzar-lo amb un valor del component |
| 5. Formulari de vuit camps amb transicions | useReducer |
05-05 | Amb molts camps i regles de transició, un reductor centralitza la lògica millor que vuit useState solts |
Solució 3.
function PanellResum({ flota }) {
const [consultes, setConsultes] = useState(0); // cel·la 0
const [missatge, setMissatge] = useState('Prem per actualitzar el resum.'); // cel·la 1
const [ultimaConsulta, setUltimaConsulta] = useState(null); // cel·la 2
const disponibles = flota.filter((bicicleta) => bicicleta.estat === 'disponible').length;
const enTaller = flota.filter((bicicleta) => bicicleta.estat === 'mantenimiento').length;
useEffect(() => {
document.title = `CicloUrbano · ${disponibles} bicis disponibles`;
}, [disponibles]);
function gestionarConsulta() {
setConsultes((consultesPrevies) => consultesPrevies + 1);
setUltimaConsulta(new Date().toLocaleTimeString('ca-ES'));
setMissatge(
disponibles === 0
? 'No queda cap bicicleta disponible ara mateix.'
: `Hi ha ${disponibles} de ${flota.length} bicicletes llestes per llogar.`
);
}
return (
<section className={estils.panell}>
<h2>Resum de la flota</h2>
<p>Total: {flota.length} · Disponibles: {disponibles} · Al taller: {enTaller}</p>
<p aria-live="polite">{missatge}</p>
<p className={estils.comptador}>Consultes fetes: {consultes}</p>
{ultimaConsulta && <p>Última consulta: {ultimaConsulta}</p>}
<button type="button" onClick={gestionarConsulta}>Actualitzar resum</button>
</section>
);
}ultimaConsulta ocupa la cel·la 2, perquè és el tercer hook cridat. El que importa no és el número en si, sinó que aquesta posició sigui la mateixa a tots els renders: per això la declaració va al nivell superior, al costat de les altres dues, i no dins de l'if que decideix si mostrar-la. La condició s'aplica només al JSX ({ultimaConsulta && …}), mai a la crida del hook. Si l'haguessis declarat dins d'un condicional, en els renders on no es complís, useEffect passaria a llegir la cel·la equivocada.
Conclusió
Un hook és una funció que comença per use i enganxa un component de funció al sistema intern de React, donant-li accés a estat, efectes, context i tot el que abans exigia una classe. Van néixer per resoldre tres problemes concrets que arrossegaven les classes: la impossibilitat de donar estat a una funció, l'infern d'embolcalls que produïen els HOC i les render props (04-02), i la lògica d'un mateix assumpte repartida entre mètodes del cicle de vida (04-03).
Les seves dues regles —només al nivell superior i només des de components o des d'altres hooks— no són capricis d'estil: s'expliquen pel mecanisme de la llista ordenada de cel·les que React manté per instància. El vincle entre una crida a useState i el seu valor guardat és la posició, i per això el nombre i l'ordre de les crides ha de ser idèntic a tots els renders. eslint-plugin-react-hooks vigila les dues regles per tu, i convé tractar rules-of-hooks com un error. Tens també el mapa complet: quins hooks existeixen, per a què serveix cadascun i en quina lliçó s'estudia; i un primer PanellResum de CicloUrbano que combina useState amb useEffect sense cap embolcall.
Queda una peça per tancar el mòdul, i és l'excepció que confirma tot l'anterior. Els hooks han substituït els mètodes del cicle de vida en tot… menys en un cas: capturar els errors que llança un component durant el render. Si una targeta de CicloUrbano peta per una dada inesperada, React desmunta l'arbre sencer i la persona usuària es queda mirant una pàgina en blanc. Evitar-ho requereix una eina que avui dia encara exigeix un component de classe. La propera lliçó és Límits d'Error: Capturar Fallades a la Interfície.
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
