Διαχείριση των Events

Η διαχείριση των events με τα React elements είναι παρόμοια με τη διαχείριση των events στα DOM elements. Υπάρχουν μερικές διαφορές στο συντακτικό:

  • Στο React τα events ονομάζονται χρησιμοποιώντας camelCase, αντί για lowercase.
  • Με το JSX περνάτε μια συνάρτηση ως event handler, αντί για ένα string.

Για παράδειγμα, το HTML:

<button onclick="activateLasers()">
  Activate Lasers
</button>

είναι λίγο διαφορετικό στο React:

<button onClick={activateLasers}>
  Activate Lasers
</button>

Ακόμη μια διαφορά είναι ότι δεν μπορείτε να επιστρέψετε false για να σταματήσετε την προκαθορισμένη συμπεριφορά στο React. Θα πρέπει να καλέσετε τη μέθοδο preventDefault. Για παράδειγμα, με απλό HTML, για να σταματήσετε τη προκαθορισμένη συμπεριφορά του link, που ανοίγει μια νέα σελίδα, μπορείτε να γράψετε:

<a href="#" onclick="console.log('The link was clicked.'); return false">
  Click me
</a>

Στο React αυτό θα μπορούσε να είναι:

function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');
  }

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

Εδώ το e είναι ένα synthetic event. Το React ορίζει αυτά τα synthetic events σύμφωνα με το W3C spec, οπότε δεν χρειάζεται να ανησυχείτε για τη συμβατότητα μεταξύ των browsers. Δείτε τον οδηγό αναφοράς του SyntheticEvent για να μάθετε περισσότερα.

Όταν χρησιμοποιείτε το React, γενικά δεν θα χρειαστεί να καλείτε το addEventListener για να προσθέσετε listeners σε ένα DOM element μετά τη δημιουργία του. Αντί για αυτό, απλά προσθέστε ένα listener όταν το element γίνεται αρχικά rendered.

Όταν ορίζετε ένα component χρησιμοποιώντας μια ES6 κλάση, ένα κοινό μοτίβο για ένα event handler είναι να ορίζεται ως μια μέθοδος της κλάσης. Για παράδειγμα αυτό το Toggle component κάνει render ένα button που αφήνει τον χρήστη να κάνει εναλλαγή μεταξύ των “ON” και “OFF” states:

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

    // Αυτό το binding είναι απαραίτητο για να κάνει το `this` να δουλέψει στο callback
    this.handleClick = this.handleClick.bind(this);
  }

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

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

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

Δοκιμάστε το στο Codepen

Πρέπει να είστε προσεκτικοί με τη σημασία του this στα JSX callbacks. Στη JavaScript, οι μεθόδοι των κλάσεων δεν είναι bound από προεπιλογή. Αν ξεχάσετε να κάνετε bind το this.handleClick και το περάσετε στο onClick, το this θα είναι undefined όταν καλεστεί η συνάρτηση.

Αυτή η συμπεριφορά δεν σχετίζεται με το React. Αποτελεί μέρος του πως λειτουργούν οι συναρτήσεις στη JavaScript. Γενικά, για να αναφερθείτε σε μια μέθοδο χωρίς να την ακολουθούν () όπως το onClick={this.handleClick}, θα πρέπει να κάνετε bind αυτή τη μέθοδο.

Αν το να καλείτε το bind είναι ενοχλητικό, υπάρχουν δύο τρόποι που μπορείτε να το αποφύγετε. Αν χρησιμοποιείτε το πειραματικό public class fields syntax, μπορείτε να χρησιμοποιήσετε πεδία κλάσεων για να κάνετε bind τα callbacks:

class LoggingButton extends React.Component {
  // Αυτή η σύνταξη εξασφαλίζει ότι το `this` είναι bound μέσα στο handleClick.
  // Προσοχή: αυτό το συντακτικό είναι *πειραματικό*
  handleClick = () => {
    console.log('this is:', this);
  }

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

Αυτή η σύνταξη είναι ενεργοποιημένη από προεπιλογή στο Create React App.

Αν δεν χρησιμοποιήτε το συντακτικό με τα πεδία των κλάσεων, μπορείτε να χρησιμοποιήσετε ένα arrow function στο callback:

class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

  render() {
    // Αυτή η σύνταξη εξασφαλίζει ότι το `this` είναι bound στο handleClick
    return (
      <button onClick={(e) => this.handleClick(e)}>
        Click me
      </button>
    );
  }
}

Το πρόβλημα με αυτό το συντακτικό είναι ότι δημιουργείται ένα διαφορετικό callback κάθε φορά που το LoggingButton κάνει render. Στις περισσότερες περιπτώσεις αυτό είναι οκ. Ωστόσο, εάν αυτό το callback περνιέται σαν prop σε lower components, αυτά τα components μπορεί να κάνουν ένα περιττό re-rendering. Συνήθως συνηστούμε να κάνετε bind στο constructor ή χρησιμοποιώντας τη σύνταξη με τα πεδία των κλάσεων, για να αποφύγετε τέτοια προβλήματα αποδοτικότητας.

Περνώντας Arguments σε Event Handlers

Μέσα σε ένα loop είναι κοινό να θέλετε να περάσετε μια επιπλέον παράμετρο σε ένα event handler. Για παράδειγμα, αν το id είναι το row ID, καθένα από τα παρακάτω θα λειτουργούσε:

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

Οι παραπάνω δύο γραμμές είναι ισοδύναμες και χρησιμοποιούν arrow functions και το Function.prototype.bind αντίστοιχα.

Και στις δυο περιπτώσεις, το e argument που αντιπροσωπεύει το React event θα περαστεί ως δεύτερο argument μετά το ID. Με ένα arrow function, θα πρέπει να το περάσουμε συγκεκριμένα, αλλά με το bind τα ακόλουθα arguments προωθούνται αυτόματα.