Handling Events

Xử lý sự kiện với React elements khá giống với xử lý sự kiện của DOM elements. Và đây là một số cú pháp khác nhau:

  • Event của React được đặt tên với camelCase hơn là lowercase.
  • Với JSX bạn dùng function như một event handler, thay vì dùng string.

Ví dụ, với HTML:

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

có chút khác biệt trong React:

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

Khác biệt khác là bạn không thể trả về false để chặn các hành vi mặc định của React. Bạn phải gọi preventDefault một cách rõ ràng. Ví dụ, với HTML đơn giản, để chặn dùng link mặc định khi mở một page mới, bạn có thể viết:

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

Với React có thể thay thế bằng:

function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');  }
  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}

Ở đây, e là một sự kiện tổng hợp. React định nghĩa những sự kiện tổng hợp dựa trên W3C spec, vậy nên bạn không cần phải lo lắng về tương thích trình duyệt. Hãy xem hướng dẫn tham khảo về SyntheticEvent để hiểu rõ hơn.

Khi sử dụng React, thường thì bạn không cần gọi addEventListener để thêm listeners cho DOM element sau khi nó được tạo. Thay vào đó, chỉ cần cung cấp listener khi element bắt đầu được hiển thị.

Khi bạn định nghĩa một component với ES6 class, một mô hình phổ biến là trình xử lý sự kiện trở thành phương thức của một class. Ví dụ, component Toggle này hiển thị một button giúp chuyển giữa trạng thái “ON” và “OFF”:

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

    // This binding is necessary to make `this` work in the 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')
);

Bạn phải cẩn thận với ý nghĩa của “this” trong hàm callback của JSX. Trong JavaScript, class method không gán mặc định. Nếu bạn quên gán this.handleClick và truyền nó cho onClick, this sẽ bị undefined khi mà hàm được gọi.

Đây không phải là hành vi riêng của React, nó là một phần của cách function hoạt động trong JavaScript. Nói chung là, nếu bạn dẫn một phương thức mà không có () sau nó, như là onClick={this.handleClick}, bạn nên gán phương thức này (bind).

Nếu như gọi bind làm bạn khó chịu, còn có hai cách để bạn xử lý. Nếu bạn đang sử dụng public class fields syntax, bạn có thể dùng các trường class để bind các hàm callback một cách chính xác:

class LoggingButton extends React.Component {
  // This syntax ensures `this` is bound within handleClick.
  // Warning: this is *experimental* syntax.
  handleClick = () => {
    console.log('this is:', this);
  }

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

Cú pháp này được kích hoạt mặc định trong Create React App.

Nếu bạn không đang sử dụng class fields syntax, bạn có thể dùng arrow function trong callback:

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

  render() {
    // This syntax ensures `this` is bound within handleClick
    return (
      <button onClick={() => this.handleClick()}>
        Click me
      </button>
    );
  }
}

Vấn đề của cú pháp này là các callback khác nhau được tạo ra mỗi lần LoggingButton được hiển thị. Trong gần hết các trường hợp, điều này ổn. Tuy nhiên, nếu callback này được truyền như prop cho những component tiếp theo, những component đó có thể làm các re-rendering. Nói chung, chúng tôi gợi ý bạn nên binding trong constructor hoặc sử dụng class fields syntax, để tránh về vấn đề hiệu năng.

Truyền đối số cho Event Handlers
Ở trong vòng lặp, là bình thường nếu muốn truyền thêm các tham số cho event handler. Ví dụ, nếu id là hàng ID, một trong số cách sau có thể hoạt động:

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

Hai dòng trên là tương đương, và dùng arrow functions hay Function.prototype.bind một cách tương ứng.

Trong cả hai trường hợp, đối số e đại diện cho React event sẽ được truyền như đối số thứ hai sau ID. Với arrow function, chúng ta phải truyền một cách rõ ràng, nhưng với bind các đối số bất kỳ được chuyển tiếp một cách tự động.