Fonaments React

React és un framework basat en la programació declarativa de components. Cada component declara com s'ha de renderitzar en funció de les propietats d'entrada i el seu estat:

(props, state) => view

La vista només es torna a renderitzar si canvien les propietats o l'estat.

Els components pare es comuniquen amb els fills passant propietats, i els fills poden generar esdeveniments cap als pares mitjançant callbacks.

El procés de renderitzar components es realitza sobre un DOM virtual, detectant quins canvis s'han produït a l'arbre de nodes respecte el DOM anterior. Només els canvis s'enviaran al DAM real, en un procés anomenat reconciliació.

Hi ha dues formes d'escriure de components:

  • basats en classes (tradicional): s'utilitzen classes JavaScript que estenen React.Component amb mètodes del cicle de vida. Cal, almenys, definir el mètode render(), una funció pura que retorna el contingut a renderitzar.
  • basats en funcions (modern): són funcions JavaScript amb un paràmetre, les propietats, que retornen el contingut a renderitzar. Utilitzen el concepte de hook, unes crides al framework que permeten gestionar l'estat o els efectes secundaris, per exemple.

Els components responen a tres esdeveniments:

  1. Muntatge (mount): quan el component es crea i s'afegeix al DOM.
  2. Actualització (update): quan el component s'actualitza en modificar-se una propietat o estat.
  3. Desmuntatge (unmount): quan el component s'esborrar del DOM.

El flux de React és el següent:

  • Fase de renderitzat
    • Es construeixen els components a muntar (1)
    • Es renderitzen a VDOM els nous components i aquells que calgui actualitzar (1, 2)
  • Fase de commit
    • S'actualitza el DOM (real)
    • El navegador pinta el DOM actualitzat
    • Els components actualitzats i desmuntats (2, 3) fan el cleanup d'efectes
    • Els components nous i actualitzats criden els efectes (1, 2)

Toolchain

El toolchain React és un conjunt d'eines que permeten desenvolupar aplicacions React. Inclou un servidor de desenvolupament, un compilador de JavaScript (Babel), un gestor de paquets (npm o yarn) i un empaquetador de mòduls (Webpack o Vite).

Després que React va deprecar el create react app, el toolchain oficial és Vite. Vite és un empaquetador de mòduls que permet un desenvolupament més ràpid i eficient. Utilitza una arquitectura basada en mòduls ES i ofereix una experiència de desenvolupament més ràpida i fluida.

Per crear una aplicació React amb Vite cal executar la comanda següent (segons es volgui JavaScript o TypeScript):

# React + JavaScript $ npm create vite@latest my-app --template react` # React + TypeScript $ npm create vite@latest my-app --template react-ts

JSX i components

React utilitza una sintaxi estesa de JavaScript anomenada JSX. Un compilador del toolchain (Babel) s'encarrega de convertir-ho a JavaScript. Està basada en HTML, però permet codi JavaScript dins de claus {}.

Permet definir elements React, que són objectes creats amb React.createElement(). Els elements poden ser de tipus HTML o bé definits pel desenvolupador.

Conceptualment, els components són funcions de JavaScript. Accepten entrades arbitràries (anomenades props) i retornen elements de React que descriuen el que hauria d'aparèixer a la pantalla. Aquestes propietats són de només lectura, i poden incloure callbacks per a gestionar esdeveniments.

Flux de dades

Les dades de React només viatgen des del pare cap els fills (one way data flow), i ho fan mitjançant les props. Les props són un objecte immutable, mentre els estats són mutables. Però com les props són immutables, els canvis de l'estat als fills no afecten els pares.

function Child({ propName }) { return (<h1>Hello {propName}!</h1>); } function Parent() { return (<Child propName={"React"} />); }
function Child({ propName }: { propName: string }) { return (<h1>Hello {propName}!</h1>); } function Parent() { return (<Child propName={"React"} />); }

Com que un fill no pot modificar l'estat d'un pare el que fem és aixecar l'estat (lifting state up). Això permet compartir-lo i accedir a aquells components que el necessiten.

El flux invers és possible utilitzant esdeveniments enviats des dels fills als pares.

function Child({ message, callBack }) { return ( <button onClick={() => callBack("message from child!")} >{message}</button>); } function Parent() { const [message, setMessage] = useState('no message'); return ( <Child message={message} callBack={propValue => setMessage(propValue)} />); }
function Child({ message, callBack }: { message: string, callBack: () => void }) { return ( <button onClick={() => callBack("message from child!")} >{message}</button>); } function Parent() { const [message, setMessage] = useState<string>('no message'); return ( <Child message={message} callBack={propValue => setMessage(propValue)} />); }

Hooks

React modern utilitza els components basats en funcions. Un component és una funció amb props d'entrada i el contingut a mostrar de sortida, i utilitza els hooks per a definir el seu comportament.

Els hooks permeten implementar les operacions de React:

  • Definició d'estats amb useState.
  • Definició d'UI a partir dels estats amb el JSX retornat.
  • Definició de side-effect handlers amb useEffect.
  • Modificació d'un estat amb setState.
  • Creació d'un observable computat a partir d'altres observables amb useMemo.

React permet definir custom hooks per part de l'usuari. Bàsicament, es tracta de lògica amb estat reutilitzable on no hi ha JSX. S'implementen mitjançant una funció anomenada useXXX que pot contenir altres hooks i, habitualment, retornen un objecte o una tupla.

import { useState, useEffect } from 'react'; function useDelayedMessage(delay = 1000) { const [message, setMessage] = useState("Waiting..."); useEffect(() => { const timer = setTimeout(() => { setMessage("Hello, world!"); }, delay); // Cleanup the timer if the component unmounts or delay changes return () => clearTimeout(timer); }, [delay]); return message; }
import { useState, useEffect } from 'react'; function useDelayedMessage(delay: number = 1000): string { const [message, setMessage] = useState<string>("Waiting..."); useEffect(() => { const timer = setTimeout(() => { setMessage("Hello, world!"); }, delay); // Cleanup the timer if the component unmounts or delay changes return () => clearTimeout(timer); }, [delay]); return message; }