Using the State Hook

Hooks là phần bổ sung mới trong React 16.8. Chúng giúp bạn sử dụng state và các tính năng khác của React mà không cần viết class.

Trong bài giới thiệu đã sử dụng ví dụ này giúp các bạn làm quen với Hooks:

import React, { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"  const [count, setCount] = useState(0);
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

Chúng ta sẽ bắt đầu học về Hooks bằng cách so sánh phần code này với ví dụ của một class tương đương.

Ví dụ về class tương đương

Nếu bạn đã sử dụng class với React trước đó, thì có thể bạn sẽ thấy quen với đoạn code này:

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

State bắt đầu với { count: 0 }, và chúng ta tăng state.count bằng cách gọi this.setState() khi người dùng bấm vào nút. Chúng ta sẽ sử dụng đoạn trích của class này trong suốt bài viết.

Note
Bạn có thể phân vân tại sao chúng ta sử dụng bộ đếm ở đây thay vì ví dụ thực tế hơn. Điều này giúp chúng tôi tập trung vào API trong khi chúng tôi vẫn đang tạo những bước đầu với Hooks.

Hooks and Function Components

Để nhắc lại, các function component của React sẽ như thế này:

const Example = (props) => {
  // You can use Hooks here!
  return <div />;
}

hoặc thế này:

function Example(props) {
  // You can use Hooks here!
  return <div />;
}

Bạn có thể biết trước rằng đây là “stateless components”. Hiện tại chúng ta đang giới thiệu về khả năng sử dụng state của React từ đây, nên chúng tôi thích cái tên “function components”"hơn.

Hooks không hoạt động trong các class. Nhưng bạn có thể sử dụng chúng thay vì viết các class.

What’s a Hook?

Ví dụ mới của chúng tôi bắt đầu bằng cách thêm Hook - useState từ React:

import React, { useState } from 'react';
function Example() {
  // ...
}

Hook là gì? Hook là một hàm đặc biệt giúp bạn “hook into” các tính năng của React. Ví dụ, useState là Hook giúp bạn thêm state của React vào các function component. Chúng ta sẽ học về các Hook khác sau này.

Khi nào nên sử dụng Hook? Nếu bạn viết một function component và nhận ra bạn cần thêm một vài state cho nó, trước hết bạn phải chuyển nó thành một class. Bây giờ bạn có thể sử dụng Hook bên trong function component đang có. Chúng ta sẽ làm điều này ngay bây giờ!

Note:
Có một vài luật đặc biệt về nơi bạn có thể và không thể sử dụng Hooks bên trong component. Chúng ta sẽ học điều này trong phần sau.

Khai báo một biến State

Trong một class, chúng ta khởi tạo state count từ 0 bằng cách cài đặt this.state{ count: 0 } trong constructor:

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

Trong function component, chúng ta không có this, vậy nên chúng ta không thể gọi hay đọc this.state. Thay vào đó, chúng ta gọi Hook - useState một cách trực tiếp bên trong component:

import React, { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"  const [count, setCount] = useState(0);

Gọi useState để làm gì? useState định nghĩa một “state variable”. Biến của chúng ta được gọi là count nhưng chúng ta có thể gọi bằng các tên khác, như banana chẳng hạn. Đây là cách để “preserve”"vài giá trị giữa các lệnh gọi hàm - useState là một cách mới để sử dụng chính xác cùng khả năng mà this.state cung cấp cho class. Thông thường, các biến sẽ mất đi khi hàm thoát nhưng biến state được bảo toàn bởi React.

Chúng ta truyền đối số gì cho useState? Đối số duy nhất cho useState() là state khởi tạo. Không giống như với các class, state không nhất thiết phải là object. Chúng ta có thể dùng số hoặc chuỗi nếu đó là điều chúng ta cần. Trong ví dụ của chúng tôi, chúng tôi chỉ muốn số cho số lần người dùng click, nên truyền 0 như state khởi tạo cho biến của chúng tôi. ( Nếu chúng tôi muốn lưu 2 giá trị khác nhau trong state, chúng tôi sẽ gọi useState() hai lần.)

useState trả về cái gì? Nó trả về một cặp giá trị: giá trị state hiện tại và một hàm dùng để cập nhật state. Đó là lý do chúng tôi viết
const [ count, setCount ] = useState(). Điều này tương tự với this.state.count và this.setState trong class, ngoại trừ việc bạn dùng chúng như một cặp. Nếu bạn không quen với cú pháp mà chúng tôi đã dùng, chúng tôi sẽ trở lại với nó ở cuối bài.

Bây giờ khi chúng ta hiểu Hook - useState làm gì, ví dụ sẽ dễ hiểu hơn:

import React, { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

Chúng tôi khai báo biến state được gọi là count, và gán giá trị 0. React sẽ nhớ giá trị hiện tại của nó giữa các lần re-render, và cung cấp giá trị gần nhất cho function của chúng ta. Nếu chúng ta muốn cập nhật giá trị hiện tại của count, chúng ta có thể gọi setCount.

Note
Bạn có thể phân vân tại sao thay vì là useState không được đặt tên là createState?

“Create” không được sát nghĩa bởi state chỉ được tạo vào lần đầu tiên component của chúng ta render. Trong suốt cao lần render tiếp theo, useState cung cấp cho chúng ta state hiện tại. Nếu không thì không phải là “state” !. Đây cũng là lý do tại sao Hook luôn luôn bắt đầu bằng use . Chúng ta sẽ học tại sao sau này ở bài tiếp theo.

Reading State

Khi chúng ta muốn hiển thị giá trị count hiện tại trong class, chúng ta đọc this.state.count:

<p>You clicked {this.state.count} times</p>

Trong function, chúng ta có thể sử dụng count một cách trực tiếp:

  <p>You clicked {count} times</p>

Updating State

Trong một class, chúng ta cần gọi this.setState() để cập nhật state count:

 <button onClick={() => this.setState({ count: this.state.count + 1 })}>    Click me
  </button>

Trong function, chúng ta đã có sẵn setCountcount nhưng các biến nên chúng ta không cần this:

<button onClick={() => setCount(count + 1)}>    Click me
  </button>

Recap
Bây giờ hãy tóm tắt lại những gì chúng ta đã học và kiểm tra sự hiểu biết của chúng ta.

 1:  import React, { useState } from 'react';
 2:
 3:  function Example() {
 4:    const [count, setCount] = useState(0);
 5:
 6:    return (
 7:      <div>
 8:        <p>You clicked {count} times</p>
 9:        <button onClick={() => setCount(count + 1)}>
10:         Click me
11:        </button>
12:      </div>
13:    );
14:  }
  • Dòng 1: Chúng ta nhập Hook - useState từ React. Điều này giúp chúng ta lưu local state trong function component.
  • Dòng 4: Bên trong component Example, chúng ta khai báo biến state mới bằng cách gọi useState. Nó trả về một cặp giá trị, tùy chúng ta đặt tên. Chúng tra gọi biến là count bởi vì biến lưu số lần bấm nút. Chúng ta khởi tạo nó từ 0 bằng cách truyền giá trị 0 là đối số duy nhất của useState. Trả về thứ hai là một function. Nó giúp chúng ta cập nhật biến count nên chúng ta sẽ đặt tên là setCount.
  • Dòng 9: Khi người dùng bấm, chúng ta gọi setCount với giá trị mới. React sẽ re-render component Example, truyền giá trị mới của count cho hàm.

Điều này có thể là hơi nhiều khi bắt đầu. Đừng vội! Nếu như bạn đang lạc trong lời giải thích, nhìn lại phần code ở trên lần nữa và cố gắng đọc từ trên xuống dưới. Chúng tôi hứa rằng khi bạn thử “quên” cách state hoạt động ở class, và xem lại với góc nhìn mới, nó sẽ có ý nghĩa.

Tip: What Do Square Brackets Mean?

Bạn có thể nhận ra cặp ngoặc vuông khi chúng ta khai báo một biến state:

const [count, setCount] = useState(0);

Phần tên ở bên trái không phải là một phần của React API. Bạn có thể đặt tên biến state của bạn:

const [fruit, setFruit] = useState('banana');

Cú pháp JavaScript này được gọi là “array destructuring”. Điều này có nghĩa là chúng ta tạo hai biến mới fruit và setFruit, là nơi mà fruit được đặt là giá trị trả về đầu tiên của useState, và setFruit là thứ hai. Nó tương đương với đoạn code sau:

 var fruitStateVariable = useState('banana'); // Returns a pair
  var fruit = fruitStateVariable[0]; // First item in a pair
  var setFruit = fruitStateVariable[1]; // Second item in a pair

Khi chúng ta khai báo một biến state với useState, nó trả về một cặp - mảng với hai thành phần. Phần tử đầu tiên là giá trị hiện tại, và thứ hai là hàm giúp ta cập nhật nó. Sử dụng [0] và [1] để truy cập chúng có một chút nhầm lẫn bởi chúng có ý nghĩa riêng. Điều này là lý do chúng ta sử dụng array destructuring.

Tip: Using Multiple State Variables
Khai báo các biến state như một cặp [something,setSomething] cũng tiện dụng bởi vì chúng giúp chúng ta đưa ra các tên khác nhau cho các biến state khác nhau nếu chúng ta muốn nhiều hơn một:

function ExampleWithManyStates() {
  // Declare multiple state variables!
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);

Trong component ở trên, chúng ta có age, fruit, và todos như biến local, và chúng ta có thể cập nhật một cách riêng biệt:

  function handleOrangeClick() {
    // Similar to this.setState({ fruit: 'orange' })
    setFruit('orange');
  }

Bạn không cần phải sử dụng nhiều biến state. Các biến state có thể giữ nhiều object hay array đều được, nên bạn có thể gom nhóm các dữ liệu có quan hệ với nhau. Tuy nhiên, không như this.setState trong class, cập nhật một biến state luôn luôn thay thế nó thay vì ghép chúng lại.

Next Steps
Trong bài này chúng ta đã học về một trong những Hook được cung cấp bởi React, được gọi là useState. Chúng ta đoi khi cũng có thể gọi nó là “State Hook”. Nó giúp chúng ta thêm local state cho function component của React - điều mà chúng ta đã làm từ đầu!

Chúng ta cũng đã học một chút và Hooks là gì. Hooks là các hàm giúp bạn “hook into” các tính năng của React từ các function component. Tên của chúng luôn bắt đầu bằng use, và có nhiều Hooks mà chúng ta còn chưa biết.

Bây giờ hay tiếp tục học Hook tiếp theo: useEffect. Nó giúp bạn thực hiện các side effect trong các component, và nó tương tự với các lifecycle method trong class.