Gestione degli Eventi

La gestione degli eventi negli elementi React è molto simile alla gestione degli eventi negli elementi DOM. Vi sono alcune differenze sintattiche:

  • Gli eventi React vengono dichiarati utilizzando camelCase, anziché in minuscolo.
  • In JSX, il gestore di eventi (event handler) viene passato come funzione, piuttosto che stringa.

Per esempio, l’HTML:

<button onclick="attivaLasers()">
  Attiva Lasers
</button>

è leggermente diverso in React:

<button onClick={attivaLasers}>
  Attiva Lasers
</button>

Un’altra differenza è che, in React, non è possibile ritornare false per impedire il comportamento predefinito. Devi chiamare preventDefault esplicitamente. Ad esempio, in un semplice codice HTML per impedire il comportamento predefinito del link di aprire una nuova pagina, potresti scrivere:

<a href="#" onclick="console.log('Hai cliccato sul link.'); return false">
  Clicca qui
</a>

In React, invece sarebbe:

function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('Hai cliccato sul link.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Clicca qui
    </a>
  );
}

In questo esempio, il parametro e è un evento sintetico (synthetic event). React definisce questi eventi sintetici in base alle specifiche W3C, quindi non hai bisogno di preoccuparti della compatibilità tra browser. Consulta la guida di riferimento SyntheticEvent per saperne di più.

Usando React, in generale, non dovresti aver bisogno di chiamare addEventListener per aggiungere listeners ad un elemento DOM dopo la sua creazione. Invece, basta fornire un listener quando l’elemento è inizialmente renderizzato.

Quando definisci un componente usando una classe ES6, un pattern comune è usare un metodo della classe come gestore di eventi. Ad esempio, questo componente Interruttore renderizza un pulsante che consente all’utente di alternare gli stati “Acceso” e “Spento”:

class Interruttore extends React.Component {
  constructor(props) {
    super(props);
    this.state = {acceso: true};

    // Necessario per accedere al corretto valore di `this` all'interno della callback
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(state => ({
      acceso: !state.acceso
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.acceso ? 'Acceso' : 'Spento'}
      </button>
    );
  }
}

ReactDOM.render(
  <Interruttore />,
  document.getElementById('root')
);

Prova su CodeSandbox

Fai attenzione al valore di this nelle callback JSX. In JavaScript, i metodi delle classi non sono associati (bound) di default. Se dimentichi di applicare bind a this.handleClick e di passarlo a onClick, this sarà undefined quando la funzione verrà effettivamente chiamata.

Questo non è un comportamento specifico in React: è parte di come funzionano le funzioni in JavaScript. In generale, se ti riferisci ad un metodo senza () dopo di esso, per esempio onClick = {this.handleClick}, potresti aver bisogno di applicare bind a quel metodo.

Se usare la chiamata al metodo bind ti sembra troppo, ci sono due alternative a disposizione. Puoi usare la sintassi sperimentale proprietà pubbliche delle classi, utilizzando le proprietà delle classi per associare correttamente le callback:

class LoggingButton extends React.Component {
  // Garantisce che `this` si riferisca all'oggetto originale all'interno di handleClick.
  // Attenzione: questa è sintassi *sperimentale*.
  handleClick = () => {
    console.log('Il valore di `this` è: ', this);
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Clicca qui
      </button>
    );
  }
}

Questa sintassi è abilitata nelle impostazioni predefinite di Create React App.

Se non stai usando la sintassi delle proprietà delle classi, è possibile utilizzare una funzione a freccia all’interno della callback:

class LoggingButton extends React.Component {
  handleClick() {
    console.log('Il valore di `this` è: ', this);
  }

  render() {
    // Questa sintassi garantisce che `this` sia associato correttamente all'interno di handleClick
    return (
      <button onClick={(e) => this.handleClick(e)}>
        Clicca qui
      </button>
    );
  }
}

Il problema con questa sintassi è che viene creata una callback diversa ogni volta che LoggingButton viene renderizzato. Nella maggior parte dei casi, non vi sono problemi. Tuttavia, se questa callback viene passata come prop a componenti inferiori, tali componenti potrebbero eseguire un ulteriore re-renderizzamento. In generale, vi consigliamo di utilizzare bind nel costruttore o la sintassi delle proprietà pubbliche nelle classi, per evitare questo tipo di problema di prestazioni.

Passare Argomenti ai Gestori di Eventi

All’interno di un ciclo, è comune avere l’esigenza di passare un parametro aggiuntivo ad un gestore di eventi. Ad esempio, avendo id come l’identificativo della riga, le seguenti dichiarazioni sarebbero entrambe valide:

<button onClick={(e) => this.deleteRow(id, e)}>Elimina riga</button>
<button onClick={this.deleteRow.bind(this, id)}>Elimina riga</button>

Le due linee di codice precedenti sono equivalenti e utilizzano le funzioni a freccia e Function.prototype.bind rispettivamente.

In entrambi i casi, l’argomento e, che rappresenta l’evento React, verrà passato come secondo argomento dopo l’ID. Con la funzione a freccia, devi passarlo esplicitamente, mentre con bind qualsiasi altro argomento viene passato automaticamente.