openplanning

Xử lý sự kiện (Event) trong ReactJS

  1. ReactJS Events
  2. Event của Component con
  3. Một ví dụ phức tạp
Trong bài học này tôi sẽ hướng dẫn bạn "Làm sao để sử dụng Event (sự kiện) trong ReactJS". Cũng không có nhiều khác biệt khi bạn sử dụng các event trong ReactJS so với sử dụng các event trong Javascript.
  • Trong Javascript bạn sẽ xử lý sự kiện trong một hàm, còn trong React bạn sẽ xử lý sự kiện trong một phương thức của Component.

1. ReactJS Events

React là một thư viện dựa trên Javascript, về cơ bản không có nhiều khác biệt trong cách xử lý sự kiện giữa ReactJSJavascript. Với Javascript, khi sự kiện xẩy ra một hàm sẽ được gọi để thực thi. Nhưng với React, khi sự kiện xẩy ra, sẽ có một phương thức của Component được gọi.
OK, trước hết hãy xem một ví dụ đơn giản:
onClick-example.html
class CurrentTime extends React.Component {
  constructor(props) {
    super(props);

    var now = new Date();
    this.state = {
      currentTime: now.toString()
    };
  }

  // A method of CurrentTime component
  refreshCurrentTime() {
    var now = new Date();
    this.setState({ currentTime: now.toString() });
  }
  render() {
    return (
      <div>
        <h4>Current Time:</h4>
        <p>{this.state.currentTime}</p>
        <button onClick={() => this.refreshCurrentTime()}>
          Refresh Current Time
        </button>
      </div>
    );
  }
}

// Render
ReactDOM.render(<CurrentTime />, document.getElementById("currenttime1"));
onClick-example.html
<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="UTF-8">
      <title>ReactJS Event</title>
      <script src="https://unpkg.com/react@16.4.2/umd/react.production.min.js"></script>
      <script src="https://unpkg.com/react-dom@16.4.2/umd/react-dom.production.min.js"></script>
      <script src="https://unpkg.com/babel-standalone@6.26.0/babel.min.js"></script>
      <style>
         #currenttime1 {
         border:1px solid blue;
         padding: 5px;
         margin-top: 20px;
         }
      </style>
   </head>
   <body>
      <h3>ReactJS Event: onClick</h3>

      <div id="currenttime1"></div>

      <script src="onClick-example.jsx" type="text/babel"></script>

   </body>
</html>
Arrow Function
Với cú pháp Javascript ES6 bạn có thể tạo một arrow function (Hàm mũi tên), rất ngắn gọn và dễ hiểu:
** arrow function **
// Normal function with parameters
var myfunc = function(param1, param2)  {
   // Statements
};

// Arrow function with parameters.
var arrfunc = (param1, param2) => {
   // Statements
};


// Normal function without paramters.
var myfunc2 = function( )  {
   // Statements
};

// Arrow function without parameters.
var arrfunc2 = ( ) => {
   // Statements
};

// If function have only one statement.
var arrfunc2 = ( ) => singleStatement;
onClick, onChange,..
onClick phải gọi đến một hàm Javascript, nó không thể gọi trực tiếp đến một phương thức của Component. Vì vậy onClick nên gọi đến một hàm nặc danh (anonymous), và bên trong hàm nặc danh này bạn có thể gọi đến phương thức của Component:
Arrow function với tham số event:
// A method of Component with event parameter.
refreshCurrentTime(event) {
  var now = new Date();
  this.setState({ currentTime: now.toString() });
}

render() {
  return (
    <div>
      <h4>Current Time:</h4>
      <p>{this.state.currentTime}</p>
      <button onClick={(event) => this.refreshCurrentTime(event)}>
          Refresh Current Time
      </button>
    </div>
  );
}
Với Javascript ES6 bạn cũng có thể gọi đến phương thức của Component theo cách sau:
.jsx
// A method of this Component.
refreshCurrentTime(event) {
  var now = new Date();
  this.setState({ currentTime: now.toString() });
}

render() {
  return (
    <div>
      <h4>Current Time:</h4>
      <p>{this.state.currentTime}</p>
      <button onClick={this.refreshCurrentTime.bind(this,event)}>
         Refresh Current Time
      </button>
    </div>
  );
}
Hoặc:
onClick-example3.jsx
class CurrentTime extends React.Component {
  constructor(props) {
    super(props);

    var now = new Date();
    this.state = {
      currentTime: now.toString()
    };

    // (***)
    this.refreshCurrentTime = this.refreshCurrentTime.bind(this);
  }

  // A method of this Component
  refreshCurrentTime( ) {
    var now = new Date();
    this.setState({ currentTime: now.toString() });
  }
  render() {
    return (
      <div>
        <h4>Current Time:</h4>
        <p>{this.state.currentTime}</p>
        <button onClick={this.refreshCurrentTime}>
          Refresh Current Time
        </button>
      </div>
    );
  }
}

// Render
ReactDOM.render(<CurrentTime />, document.getElementById("currenttime1"));

2. Event của Component con

Một sự kiện xẩy ra tại Component con, nhưng bạn muốn gọi đến một phương thức tại Componen cha, dưới đây là một ví dụ như thế.
childEvent-example.jsx
// <Child> with 1 attribute: myClickHandler
// <Child myClickHandler=.. />
class Child extends React.Component {
  render() {
    return (
      <div className="child">
        <h4>Child</h4>
        <button onClick={this.props.myClickHandler}>Click me!</button>
      </div>
    );
  }
}

class Parent extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      count: 0
    };
  }

  myMethodOfParent() {
    this.setState((prevState, props) => {
      return {
        count: prevState.count + 1
      };
    });
  }

  render() {
    return (
      <div className="parent">
        <h3>(Parent) Clicked: {this.state.count} </h3>
        <Child myClickHandler={() => this.myMethodOfParent()} />
      </div>
    );
  }
}

// Render
ReactDOM.render(<Parent />, document.getElementById("placeholder1"));
childEvent-example.html
<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="UTF-8">
      <title>ReactJS Event</title>
      <script src="https://unpkg.com/react@16.4.2/umd/react.production.min.js"></script>
      <script src="https://unpkg.com/react-dom@16.4.2/umd/react-dom.production.min.js"></script>
      <script src="https://unpkg.com/babel-standalone@6.26.0/babel.min.js"></script>
      <style>
         .parent {
         border:1px solid #d9ebe8;
         padding: 5px;
         margin-top: 20px;
         }
         .child {
         width:150px;
         border:1px solid #8cb0aa;
         margin: 5px;
         padding: 5px;
         }
      </style>
   </head>
   <body>
      <h3>The child's event calls to parent's method</h3>

      <div id="placeholder1"></div>

      <script src="childEvent-example.jsx" type="text/babel"></script>
      
   </body>
</html>

3. Một ví dụ phức tạp

OK, và bây giờ là một ví dụ phức tạp hơn, nó yêu cầu bạn thông thạo nhiều kiến thức trong ReactJS, chẳng hạn Lists, Props, State, Event..
complex-example.jsx
// <OrderDetail> with 4 attributes:
// productName, price, quantity, addHandler
class OrderDetail extends React.Component {
  render() {
    return (
      <div className="order-detail">
        <h4>{this.props.productName}</h4>
        <p>Price: {this.props.price} USD</p>
        <p>Quantity: {this.props.quantity}</p>
        <p>
          <button onClick={this.props.addHandler}>+</button>
        </p>
      </div>
    );
  }
}

class Order extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      amount: 0,
      details: [
        { id: 1, productName: "IPhone X", price: 900, quantity: 0 },
        { id: 2, productName: "Samsung S9", price: 800, quantity: 0 },
        { id: 3, productName: "Nokia 8", price: 650, quantity: 0 }
      ]
    };
  }

  updateOrder(index) {
    this.setState((prevState, props) => {
      console.log(this.state.details);

      var newQty = prevState.details[index].quantity + 1;
      this.state.details[index].quantity = newQty;
      this.state.amount = prevState.amount + 1 * prevState.details[index].price;
      return {
        amount: this.state.amount,
        details: this.state.details
      };
    });
  }

  render() {
    // Array of <OrderDetail ../>
    var detailTags = this.state.details.map((e, index) => (
      <OrderDetail
        key={e.id}
        addHandler={() => this.updateOrder(index)}
        productName={e.productName}
        price={e.price}
        quantity={e.quantity}
      />
    ));
    return (
      <div className="order">
        {detailTags}
        <div className="clear" />
        <p className="total">Total: <b>{this.state.amount} USD</b></p>
      </div>
    );
  }
}

// Render
ReactDOM.render(<Order />, document.getElementById("order1"));
complex-example.html
<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="UTF-8">
      <title>ReactJS Event</title>
      <script src="https://unpkg.com/react@16.4.2/umd/react.production.min.js"></script>
      <script src="https://unpkg.com/react-dom@16.4.2/umd/react-dom.production.min.js"></script>
      <script src="https://unpkg.com/babel-standalone@6.26.0/babel.min.js"></script>
      <style>
         .order {
           border:1px solid #d9ebe8;
           padding: 5px;
           margin-top: 20px;
         }
         .order-detail {
            float : left;
            width:150px;
            border:1px solid #8cb0aa;
            margin: 5px;
            padding: 5px;
         }
         .clear {
           clear: both;
         }
         .total {
            margin: 5px;
         }
      </style>
   </head>
   <body>
      <h3>Complex Example (Event, List, Props, State)</h3>

      <div id="order1"></div>

      <script src="complex-example.jsx" type="text/babel"></script>

   </body>
</html>