Penanganan Events

Menangani events dengan elemen React sangat mirip seperti menangani sebuah events pada elemen DOM. Ada beberapa perbedaan sintaksis:

  • Events pada React biasanya ditulis dalam bentuk camelCase, bukan lowercase.
  • Dengan JSX Anda dapat mengoper function sebagai event handler, bukan sebagai string.

Sebagai contoh pada HTML berikut ini:

<button onclick="activateLasers()">
  Aktivasi Laser
</button>

sedikit berbeda dengan React:

<button onClick={activateLasers}>
  Aktivasi Laser
</button>

Perbedaan lainnya adalah Anda tidak dapat mengembalikan nilai false untuk mencegah behavior bawaan React. Anda harus memanggil preventDefault secara eksplisit. Sebagai contoh, pada HTML untuk mencegah agar link bawaan membuka halaman baru, Anda dapat menulis seperti ini:

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

Sedangkan pada React, contoh tersebut dapat ditulis sebagai berikut:

function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('Tautan diklik.');
  }

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

Di sini, e adalah sebuah event tiruan. React mendefinisikan event tiruan ini berdasarkan W3C spec, jadi Anda tidak perlu khawatir akan kesesuaian antar lintas browser. Lihat referensi pada SyntheticEvent untuk panduan belajar lebih jauh.

Ketika menggunakan React, pada umumnya Anda tidak perlu memanggil addEventListener untuk menambahkan listener pada elemen DOM setelah dibuat. Sebagai gantinya, cukup berikan listener ketika elemen pertama kali di-render.

Ketika Anda mendefinisikan sebuah komponen dengan menggunakan ES6 class, pada umumnya penanganan event sebagai sebuah method dalam class. Sebagai contoh, komponen Toggle ini me-render sebuah tombol yang memungkinkan pengguna untuk mengubah kondisi “ON” dan “OFF” pada sebuah state:

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

    // cara binding seperti ini diperlukan untuk membuat `this` dapat berfungsi -
    // pada callback binding
    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')
);

Coba di CodePen

Anda harus berhati-hati terhadap makna dari this pada JSX callbacks. Dalam JavaScript, class method tidak terikat secara bawaan. Jika Anda lupa untuk melakukan binding this.handleClick dan mengoperkan pada onClick, maka this akan menjadi undefined ketika sebuah function telah dipanggil.

Ini bukan behavior yang spesifik pada React, tetapi ini merupakan bagian dari bagaimana functions dalam JavaScript bekerja. Pada umumnya jika Anda mendefinisikan sebuah method tanpa diakhiri dengan (), seperti onClick={this.handleClick}, maka Anda harus melakukan binding terhadap method tersebut.

Jika Anda tidak terbiasa menggunakan bind, ada dua cara untuk mengatasi ini. Jika Anda menggunakan sintaksis eksperimental public class fields, Anda dapat menggunakan class fields untuk melakukan binding terhadap callbacks:

class LoggingButton extends React.Component {
  // Sintaksis ini memastikan `this` telah terikat dalam handleClick.
  // Peringatan: Ini adalah eksperimental sintaksis.
  handleClick = () => {
    console.log('this is:', this);
  }

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

Sintaksis ini telah diakfifkan secara bawaan pada Create React App.

Jika Anda tidak menggunakan sintaksis class fields, Anda dapat menggunakan arrow function pada callback:

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

  render() {
    // Sintaksis ini memastikan `this` telah terikat dalam handleClick.
    return (
      <button onClick={(e) => this.handleClick(e)}>
        Klik Saya
      </button>
    );
  }
}

Masalah pada sintaksis tersebut adalah callback yang berbeda dibuat setiap kali LoggingButton di-render. Dalam banyak kasus, hal ini tidak masalah. Akan tetapi, jika callback tersebut mengoperkan sebagai props kepada komponen yang lebih rendah, maka komponen tersebut mungkin akan melakukan ekstra render ulang. Kita umumnya merekomendasikan binding dilakukan pada constructor atau menggunakan sintaksis class fields, untuk menghindari masalah kinerja seperti ini.

Mengoper Argumen ke dalam Penanganan Event

Di dalam perulangan biasanya ingin mengoper sebuah parameter ekstra kedalam penanganan event. Sebagai contoh, jika id sama dengan baris ID, maka salah satu dari kedua dapat dijalankan:

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

Dua baris di atas memiliki arti yang sama, masing-masing menggunakan arrow functions dan Function.prototype.bind.

Dalam kedua kasus tersebut, argumen e merepresentasikan event React yang akan dioper sebagai argumen kedua setelah ID. Dengan arrow function, kita harus mengeoperkan secara eksplisit, namun dengan bind segala argumen setelahnya akan diteruskan secara otomatis.