Frontend

Come vengono gestiti gli eventi in React

Autore

Manuel Ricci

Gli eventi in React sono un aspetto fondamentale per gestire le interazioni utente all'interno delle applicazioni. React implementa un sistema di eventi sintetici che incapsula i nativi eventi del browser, offrendo un'interfaccia coerente attraverso diversi browser e migliorando le prestazioni tramite un meccanismo di delegazione globale degli eventi.

Cosa sono gli eventi sintetici in React?

Gli eventi sintetici sono oggetti wrapper che React utilizza per incapsulare gli eventi nativi del browser. Forniscono un'API uniforme attraverso tutti i browser, normalizzando le differenze che possono esistere tra i vari ambienti. Ciò significa che, indipendentemente dal browser utilizzato, l'evento sintetico si comporterà nello stesso modo.

Perché React Utilizza Eventi Sintetici?

  • Compatibilità Cross-Browser: Gli eventi sintetici astraggono le differenze tra i diversi browser, fornendo agli sviluppatori una singola API per la gestione degli eventi che funziona in modo uniforme su tutti i browser supportati.
  • Prestazioni: Grazie al sistema di delegazione globale, React può minimizzare il numero di listener di eventi diretti necessari nel DOM. Questo approccio migliora le prestazioni riducendo il carico di memoria e aumentando la velocità di setup degli event listeners.
  • Pooling degli Eventi: React implementa un sistema di "pooling" per gli eventi sintetici. Questo significa che, dopo che un evento viene gestito, l'oggetto evento sintetico viene riutilizzato per eventi futuri. Ciò riduce il numero di allocazioni di memoria necessarie, contribuendo a migliorare le prestazioni.

API degli Eventi Sintetici

Gli eventi sintetici in React supportano le stesse proprietà e metodi degli eventi nativi, come stopPropagation(), preventDefault(), target, currentTarget, e molti altri. React aggiunge anche alcune estensioni specifiche, come nativeEvent, che fornisce accesso all'evento nativo del browser sottostante.

Esempi di Eventi Sintetici

React fornisce eventi sintetici per una vasta gamma di azioni utente e notifiche del browser, tra cui:

  • Eventi del mouse: onClick, onMouseDown, onMouseUp, onMouseMove, ecc.
  • Eventi di input: onChange, onInput, onSubmit, ecc.
  • Eventi di focus: onFocus, onBlur.
  • Eventi della tastiera: onKeyDown, onKeyPress, onKeyUp.
  • E molti altri, coprendo praticamente ogni tipo di interazione possibile nel browser.

Accesso all'Evento Nativo

Sebbene gli eventi sintetici coprano la maggior parte delle esigenze, talvolta può essere necessario accedere direttamente all'evento del browser sottostante. Questo può essere fatto tramite la proprietà nativeEvent dell'evento sintetico:

1import React from 'react';
2
3const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
4  console.log(event.nativeEvent); // Accesso all'evento nativo del browser
5};

Delegazione degli Eventi

La delegazione degli eventi è una tecnica fondamentale nella gestione degli eventi, non solo in React, ma anche in JavaScript in generale. Questo approccio consente di sfruttare il flusso degli eventi attraverso il DOM per intercettare eventi su elementi multipli senza dover assegnare un event listener a ciascuno di essi individualmente. React implementa questa strategia a livello del proprio sistema di eventi sintetici per offrire prestazioni ottimizzate e una gestione degli eventi più semplice e potente.

Come Funziona la Delegazione degli Eventi in React

React attacca event listeners ai nodi di root dell'applicazione (solitamente il nodo dove l'app viene montata con ReactDOM.render()). Quando un evento si verifica su un elemento del DOM, l'evento "bolle" verso l'alto attraverso il DOM fino a raggiungere il nodo root. React poi intercetta questi eventi bolle e determina l'origine dell'evento basandosi sul suo Virtual DOM. Una volta identificato l'elemento sorgente e l'event handler associato, React esegue l'handler.

Vantaggi della Delegazione degli Eventi

  • Miglioramento delle Prestazioni: Assegnando un unico event listener al nodo root anziché molteplici listener a diversi nodi, si riduce il carico sulla memoria e si migliorano le prestazioni complessive dell'applicazione. Questo è particolarmente vantaggioso in applicazioni grandi e complesse.
  • Gestione Dinamica degli Elementi: La delegazione degli eventi funziona bene con gli elementi del DOM che vengono aggiunti o rimossi dinamicamente. Poiché il listener è sul nodo root, non c'è bisogno di aggiungere o rimuovere listener ogni volta che un elemento viene aggiunto o rimosso.
  • Semplificazione del Codice: Riducendo il numero di event listeners necessari, la delegazione degli eventi semplifica la struttura del codice di gestione degli eventi, rendendo l'applicazione più facile da mantenere.

Esempio di Delegazione degli Eventi

Consideriamo un elenco di elementi su cui vogliamo intercettare i click. Invece di assegnare un listener di click a ogni elemento dell'elenco, possiamo semplicemente assegnare un listener al loro contenitore comune e usare la delegazione degli eventi per gestire i click.

In React, questo concetto è astratto e gestito internamente dal sistema di eventi sintetici, ma il principio di base è lo stesso. React ascolta gli eventi al livello più alto e li delega ai componenti appropriati basandosi sul suo Virtual DOM.

Creiamo un esempio in React con TypeScript dove applichiamo il concetto di delegazione degli eventi su un elenco di elementi. Supponiamo di avere un elenco di nomi e vogliamo registrare un messaggio nella console ogni volta che un nome viene cliccato. Invece di assegnare un event listener a ogni elemento dell'elenco, assegniamo un singolo listener al contenitore dell'elenco e usiamo l'evento cliccato per determinare quale elemento è stato effettivamente cliccato.

Passaggio 1: Definire il Componente Lista

Iniziamo definendo un componente NameList che renderizza un elenco di nomi all'interno di un elemento <ul>. Aggiungeremo un listener di click al <ul> per gestire i click su tutti gli <li>:

1import React from 'react';
2
3// Definizione dell'interfaccia per le props del componente
4interface NameListProps {
5  names: string[];
6}
7
8const NameList: React.FC<NameListProps> = ({ names }) => {
9  // Handler per il click che usa la delegazione
10  const handleListClick = (event: React.MouseEvent<HTMLUListElement>) => {
11    // Determinare l'elemento cliccato
12    const target = event.target as HTMLElement;
13    
14    // Verificare se l'elemento cliccato è un LI
15    if (target.tagName === 'LI') {
16      console.log(`Hai cliccato su: ${target.textContent}`);
17    }
18  };
19
20  return (
21    <ul onClick={handleListClick}>
22      {names.map((name, index) => (
23        <li key={index}>{name}</li>
24      ))}
25    </ul>
26  );
27};
28
29export default NameList;

Passaggio 2: Utilizzo del Componente

Dopo aver definito il componente NameList, possiamo utilizzarlo all'interno dell'app principale o di un altro componente per mostrare un elenco di nomi:

1import React from 'react';
2import ReactDOM from 'react-dom';
3import NameList from './NameList';
4
5const App: React.FC = () => {
6  const names = ['Alice', 'Bob', 'Charlie', 'Diana'];
7
8  return (
9    <div>
10      <h1>Clicca su un nome:</h1>
11      <NameList names={names} />
12    </div>
13  );
14};
15
16ReactDOM.render(<App />, document.getElementById('root'));

Spiegazione

In questo esempio, NameList è un componente che accetta un array di stringhe (names) e le rende come una lista di elementi <li>. Abbiamo assegnato un event listener all'elemento <ul>, che è il contenitore comune di tutti gli elementi dell'elenco. Quando si clicca su un elemento <li>, l'evento "bolle" fino al <ul>, dove viene intercettato dal nostro event listener.

Usiamo event.target per ottenere l'elemento effettivamente cliccato e controlliamo se è un elemento <li> confrontando il suo tagName. Se è così, stampiamo il contenuto testuale (textContent) dell'elemento cliccato. Questo esempio dimostra come implementare la delegazione degli eventi in React con TypeScript, consentendo di gestire gli eventi su molti elementi con un singolo event listener.

Considerazioni sull'Uso della Delegazione degli Eventi

Anche se la delegazione degli eventi offre numerosi vantaggi, ci sono casi in cui potrebbe non essere l'approccio migliore. Ad esempio, eventi che non "bollono" (come focus e blur in certi contesti) potrebbero non essere gestiti efficacemente tramite delegazione. Inoltre, in situazioni in cui un evento deve essere intercettato precisamente sul target senza propagazione, potrebbe essere necessario assegnare direttamente un listener all'elemento specifico.

Gestire gli eventi

Gestire eventi in React usando TypeScript aggiunge un livello di tipizzazione statica che migliora l'autocompletamento del codice, la leggibilità e la sicurezza del tipo. La gestione degli eventi rimane sostanzialmente la stessa rispetto a JavaScript, ma con l'aggiunta di tipi per eventi e handler per una maggiore precisione e controllo.

Esempio Base: Gestione del Click

Ecco un esempio di come si può gestire un evento di click su un bottone in React utilizzando TypeScript:

1import React from 'react';
2
3const App: React.FC = () => {
4  const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
5    console.log('Bottone cliccato', event);
6  };
7
8  return <button onClick={handleClick}>Cliccami</button>;
9};
10
11export default App;

In questo esempio, React.MouseEvent<HTMLButtonElement> specifica il tipo dell'evento che ci aspettiamo nel nostro handler di click. HTMLButtonElement indica che l'evento di click proviene da un elemento <button>.

Esempio Avanzato: Form con Gestione degli Eventi

Vediamo ora un esempio più complesso che coinvolge la gestione degli eventi in un form, inclusi l'input di testo e la submission del form:

1import React, { useState } from 'react';
2
3interface FormState {
4  username: string;
5}
6
7const Form: React.FC = () => {
8  const [formState, setFormState] = useState<FormState>({ username: '' });
9
10  const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
11    setFormState({
12      ...formState,
13      [event.target.name]: event.target.value,
14    });
15  };
16
17  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
18    event.preventDefault();
19    console.log('Form inviato', formState);
20  };
21
22  return (
23    <form onSubmit={handleSubmit}>
24      <label htmlFor="username">Username:</label>
25      <input
26        type="text"
27        id="username"
28        name="username"
29        value={formState.username}
30        onChange={handleInputChange}
31      />
32      <button type="submit">Invia</button>
33    </form>
34  );
35};
36
37export default Form;

In questo esempio, usiamo React.ChangeEvent<HTMLInputElement> per tipizzare l'evento passato all'handleInputChange, indicando che ci aspettiamo un evento di cambio su un elemento di input. Per l'evento di submission del form, usiamo React.FormEvent<HTMLFormElement> per specificare che gestiremo un evento di form su un elemento <form>.

Considerazioni sull'Uso dei Tipi negli Eventi

L'uso di TypeScript con React per gestire gli eventi fornisce diversi vantaggi:

  • Sicurezza dei Tipi: Riduce gli errori a runtime verificando che gli eventi e i loro dati siano gestiti correttamente durante la fase di sviluppo.
  • Autocompletamento e Intellisense: L'IDE può offrire suggerimenti migliori e autocompletamento per le proprietà degli eventi e i tipi di eventi disponibili.
  • Leggibilità del Codice: Avere tipi espliciti rende il codice più facile da leggere e mantenere, specialmente in progetti grandi o in team.

L'integrazione di TypeScript in React per la gestione degli eventi non solo migliora la qualità del codice ma anche l'esperienza di sviluppo complessiva, fornendo un framework robusto per costruire interfacce utente interattive e reattive.

Performance e Best Practices

Sebbene React ottimizzi le prestazioni con la delegazione degli eventi, è comunque importante seguire alcune best practices per evitare problemi di performance, come:

  • Evitare la definizione di funzioni inline negli event handlers dentro il metodo render o all'interno del corpo del componente funzionale, poiché ciò può portare a render inutili.
  • Utilizzare il throttling o il debouncing per limitare il numero di chiamate agli event handlers in risposta a eventi ad alta frequenza come scroll o resize.
  • Quando possibile, preferire componenti funzionali e Hooks per una gestione degli eventi più pulita e moderna.

In sintesi, il sistema di eventi di React offre un potente strumento per gestire interazioni complesse in modo performante ed efficace, sfruttando concetti come eventi sintetici e delegazione degli eventi.

Caricamento...

Diventiamo amici di penna? ✒️

Iscriviti alla newsletter per ricevere una mail ogni paio di settimane con le ultime novità, gli ultimi approfondimenti e gli ultimi corsi gratuiti puubblicati. Ogni tanto potrei scrivere qualcosa di commerciale, ma solo se mi autorizzi, altrimenti non ti disturberò oltre.

Se non ti piace la newsletter ti ricordo la community su Discord, dove puoi chiedere aiuto, fare domande e condividere le tue esperienze (ma soprattutto scambiare due meme con me). Ti aspetto!

Ho in previsione di mandarti una newsletter ogni due settimane e una commerciale quando capita.