Handle events in Reactjs

Hello mọi người, đây là bài viết thứ 3 trong seri tìm hiểu về reactjs của mình. Trong bài viết trước mình đã giới thiệu sơ qua về State, Props và vòng đời của component, hôm nay chúng ta sẽ cùng tìm hiểu về cách handle event trong Reactjs nha. OK chúng ta bắt đầu ngay thôi nào 😀

Event

Về cơ bản thì việc bắt các sự kiện trong React elements thì cũng tương tự như với việc bắt sự kiện trong DOM elements. Nhưng chúng có một số điểm khác biệt như sau:

  • Tên của các sự kiện trong React sử dụng kiểu camelCase, trong khi DOM sử dụng lowercase.
  • Với JSX thì bạn sẽ truyền vào một function để xử lý các sự kiện đó

Ví dụ, khi sử dụng HTML:

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

sẽ có một chút khác biệt so với React:

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

Một điểm khác biệt nữa là bạn không thể return false để thẻ link hay button submit có thể prevent default (thông thường trong HTML thuần, khi bạn click vào một link thì nó sẽ chuyển hướng luôn đến trang mới, bạn có thể gán prevent default cho thẻ link đó và khi đó bạn có thể bắt sự kiện onclick và xử lý trong đó trước khi chuyển hướng sang trang mới) trong React bạn phải gọi đến method preventDefault. Ví dụ, khi sử dụng HTML thuần bạn có thể prevent default bằng cách return false trong thẻ link:

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

Khi sử dụng React:

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

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

e là tổng hợp của sự kiện. Nó được định nghĩa tại W3C spec, nên bạn không cần phải lo lắng về sự tương thích đối với các trình duyệt khác nhau. Các sự kiện trong React không hoạt động giống như native events. Bạn có thể tìm đọc thêm tại Synthetic Event. When sử dụng React, bạn không cần gọi đến phương thức addEventListener để thêm listeners vào DOM element sau khi sự kiện được tạo. Thay vào đó, chỉ cần cung cấp một listener khi element được render. Khi bạn định nghĩa một component sử dụng ES6, thông thường sẽ truyền vào event một method của class. Ví dụ, component Toggle render một button cho phép người dùng toggle giữa ONOFF.

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 nên cẩn thận khi sử dụng this trong JSX callback. Trong Javasctipt, class method không được mặc định bind. Nếu bạn không bind this.handleClick, this sẽ báo lỗi undefined khi function được gọi. Nó không phải là trường hợp đặt biệt trong Reactjs, nó là một phần của how functions work in Javascript. Thông thường, nếu bạn gọi đến một method mà không có () như là onClick={this.handleClick}, bạn nên bind method đó. Nếu bạn không muốn sử dụng bind, có 2 cách khác để xử lý trường hợp này. Bạn có thể sử dụng class fields để thay thế cho bind:

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>
    );
  }
}

Create React App cho phép bạn sử dụng syntax trên. Còn nếu bạn không sử dụng class fields syntax, bạn có thể sử dụng arrow functiontrong 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 đề khi sử dụng arrow function như trên là mỗi lần LoggingButton được render thì các callback khác nhau lại được khởi tạo. Trong hầu hết trường hợp thì nó sẽ không bị ảnh hưởng. Tuy nhiên, nếu callback được truyền như là một props từ component cha thì những component đó có thể được render lại nhiều lần. Nên mình khuyên mọi người nên sử dụng binding trong constructor hoặc sử dụng class fields syntax để tránh các vấn đề về performance.

Passing arguments to Event Handlers

Ví dụ trong loop, bạn sẽ thường xuyên muốn truyền các argument vào event handler. Ví dụ:

<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 nhau. Trong cả hai trường hợp, e argument đại diện cho sự kiện onClick sẽ được truyền vào như là argument thứ 2, sau ID. Đối với arrow function, chúng ta cần truyền trực tiếp, nhưng khi sử dụng bind nó sẽ được tự động truyền vào.

Tài liệu tham khảo

Âu ki chào các bạn, hôm nay chúng ta đến đây thôi nha 👍


All Rights Reserved