Hooks at a Glance

Hooks là phần bổ sung mới từ phiên bản React 16.8. Hooks 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 phải viết class.

Hooks có khả năng tương thích ngược. Phần này cung cấp cho bạn một góc nhìn tổng quan về Hooks từ các người dùng có kinh nghiệm về React. Đây là góc nhìn nhanh. Nếu bạn thấy bối rối hãy xem qua phần giới thiệu về React Hooks nhé.

State Hook

Đây là một ví dụ về hiển thị một counter. Khi bạn click vào nút, nó tăng giá trị lên:

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

Ở đây, useState là một Hook (chúng tôi sẽ nói về ý nghĩa của nó trong một khoảng khắc). Chúng ta gọi nó trong một function component để thêm vài local state cho component. React sẽ duy trì trạng thái này trong các phần render lại. useState trả về một cặp: giá trị state hiên tại và hàm giúp bạn cập nhật giá trị đó. Bạn có thể gọi hàm này từ một event handler hoặc bất cứ đâu. Nó gần giống với this.setState trong class, ngoại trừ việc nó không hợp nhất state cũ và mới. (Chúng tôi sẽ cho bạn xem một ví dụ so sánh useState và this,setState trong phần tiếp theo.)

Đối số duy nhất cho useState là state khởi tạo. Trong ví dụ ở trên, nó là 0 bởi vì counter của chúng ta bắt đầu từ 0. Chú ý rằng không giống với this.state, state này không nhất thiết phải là object - và tất nhiên nó có thể nếu bạn muốn. Đối số state khởi tao chỉ được trong lần đầu render.

Khai báo nhiều giá trị state
Bạn có thể sử dụng State Hook nhiều hơn một lần trong một component:

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

Cú pháp tách mảng giúp chúng ta cho các tên khác nhau cho các biến của state mà chúng ta đã khai báo khi gọi useState. Những tên này không phải là một phần của API useState. Thay vào đó, React cho rằng nếu bạn gọi useState nhiều lần, bạn làm điều đó cùng thứ tự trong mỗi lần render. Chúng ta sẽ quay lai cho tại sao nó hoạt động và khi nào điều này có tác dụng vào lúc sau.

Nhưng Hook là gì?

Hooks là các hàm giúp bạn “hook into” state của React và các tính năng của lifecycle từ các function component. Hook không hoạt động bên trong các class - chúng giúp bạn sử dụng React mà không cần class. (Chúng tôi không khuyến khích viết lai các component sẵn có của bạn nhưng bạn có thể bắt đầu sử dụng Hooks trong component mới nếu bạn thích.)

React cung cấp một vài Hook có sẵn như useState. Bạn cũng có thể tạo Hook riêng của mình để tái sử dụng các hành động stateful giữa các component. Trước hết chúng ta sẽ xem các Hook có sẵn.

Effect Hook

Bạn có thể đã thưc hiện lấy dữ liệu, đăng kí, hoăc thay đổi thủ công DOM từ các component của React trước đó. Chúng tôi gọi các hoạt động này là “side effects” (hoặc gọi tắt là “effects”) bởi vì chúng có thể ảnh hưởng các component khác và không thể xong trong khi đăng hiển thị.

Effect Hook, useEffect, thêm khả năng thực hiện side effects từ function component. Nó phục vụ với mục đích tương tự như componentDidMount, component DidUpdate, và componentWillMount trong các React class, nhưng hợp nhất trong một API. (Chúng tôi sẽ đưa ra các ví dụ so sánh giữa useEffect với các phương thức trong phần tiếp theo.)

Ví dụ, component này lưu tiêu đề của tài liệu sau khi React cập nhật DOM:

import React, { useState, useEffect } from 'react';
function Example() {
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:  useEffect(() => {    // Update the document title using the browser API    document.title = `You clicked ${count} times`;  });
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

Khi bạn gọi useEffect, bạn cũng nói với React
chạy hàm “effect” của bạn sau khi xóa các thay đổi trong DOM. Effects được khai báo bên trong component nên chúng có thể truy cập vào state và prop của nó. Với mặc định, React xử lý các effect sau mỗi lần hiển thị - kể cả lần đầu tiên. (Chúng tôi sẽ nói thêm và só sánh chúng với các phương thức lifecycle trong phần tiếp theo.)

Effects cũng có thể tùy chỉnh một cách tùy chọn cách dọn dẹp bằng cách trả về một hàm. Ví dụ, component sử dụng effect để đăng kí trạng thái online của bạn bè, và dọn dẹp bằng các hủy đăng kí từ nó:

import React, { useState, useEffect } from 'react';

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);    return () => {      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);    };  });
  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

Trong ví dụ này, React sẽ hủy đăng kí từ ChatAPI của chúng ta khi component unmount, cũng như trước khi chạy lại hiệu ứng do render tiếp theo. (Nếu bạn muốn, vẫn có một cách nói với React dừng việc tái đăng kí nếu props.friend.id mà chúng ta truyền cho ChatAPI không đổi.)

Giống như useState, bạn có thể sử dụng nhiều hơn một effect trong một component:

function FriendStatusWithCounter(props) {
  const [count, setCount] = useState(0);
  useEffect(() => {    document.title = `You clicked ${count} times`;
  });

  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }
  // ...

Hook giúp bạn tổ chức side effects trong một component bằng những gì liên quan (như là thêm hoặc hủy đăng kí), hơn là tập trung vào tách dựa trên các phương thức lifecycle.

Quy tắc của Hooks

Hooks là các function của JavaScript, nhưng chúng ràng buộc thêm 2 luật:

  • Chỉ được gọi Hooks ở cao nhất. Không gọi Hooks trong vòng lặp, điều kiện hay các hàm lồng nhau.

  • Chỉ được goi Hooks từ các function components của React. Không gọi Hooks từ các hàm JavaScript thông thường. (Chỉ có một nơi khác có thể gọi Hooks - Hooks tùy chỉnh của riêng bạn. Chúng ta sẽ học về chúng một chút.)

Chúng tôi cung cấp một linter plugin để thực hiện những luật này một cách tự động. Chúng tôi hiểu rằng những luật này có thể làm giới hạn hoặc phức tạp lúc bắt đầu, nhưng chúng là điều thiết yếu để Hooks hoạt động tốt.

Tạo Hooks riêng của bạn

Đôi lúc, chúng ta muốn tái sử dụng vài stateful logic giữa các component. Thông thường, có hai cách để giải quyết vấn đề này: higher-order components và render props. Hooks tùy chỉnh giúp bạn làm điều này, nhưng không cần bổ sung thêm nhiều component vào hệ thống của bạn.

Đầu tiên, chúng ta sẽ phân tách logic này vào một Hook tùy chỉnh được gọi là useFriendStatus:

import React, { useState, useEffect } from 'react';

function useFriendStatus(friendID) {  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}

Hook này nhận friendID là một đối số, và trả về mỗi khi bạn của chúng ta online.

Và giờ chúng ta có thể sử dụng nó từ cả hai component:

function FriendStatus(props) {
  const isOnline = useFriendStatus(props.friend.id);
  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}
function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);
  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

Trạng thái của các component này là hoàn toàn độc lập. Hooks là cách tái sử dụng stateful logic, mà không cần state chính nó. Thật ra, mỗi lần gọi Hook có một state hoàn toàn riêng biệt - nên bạn có thể sử dụng cùng một Hook tùy chỉnh hai lần trong một component.

Hooks tùy chỉnh là một quy ước hơn là một tính năng. Nếu tên một hàm bắt đầu bằng “use” và nó gọi Hooks khác, chúng ta gọi nó là Hook tùy chỉnh. Quy tắc đặt tên useSomething là cách linter plugin của chúng tôi có thể tìm lỗi trong code đang sử dụng Hooks.

Bạn có thể viết Hooks tùy chỉnh bao quát một phạm vi lớn các use case như là xử lý form, hiệu ứng, đăng kí khai báo, bộ hẹn giờ hay thậm chí nhiều hơn nữa mà chúng ta chưa nghĩ đên. Chúng tôi luôn hứng thú để được chiêm ngưỡng các Hooks tùy chỉnh và cộng đồng React có thể tiến tới.

Các Hook khác

Có một vài Hooks có sẵn mà bạn có thể thấy hữu ích. Ví dụ, useContext giúp bạn đăng kí vào context của React mà không cần gán:

function Example() {
  const locale = useContext(LocaleContext);  const theme = useContext(ThemeContext);  // ...
}

Hay useReducer giúp bạn quản lý local state của những component phức tạp với reducer:

function Todos() {
  const [todos, dispatch] = useReducer(todosReducer);  // ...

Những bước tiếp theo

Phewww, hơi nhanh nhỉ, nếu vài điều có thể chưa có ý nghĩa hoặc bạn muốn tìm hiểu kĩ hơn, bạn có thể đọc phần tiếp theo, bắt đầu với tài liệu của State Hook.

Cuối cùng, đừng quên phần giới thiệu về Hooks, phần giải thích tại sao chúng tôi thêm Hooks và cách chúng ta sẽ bắt đầu sử dụng chúng song song với classes - mà không cần viết lại ứng dụng của chúng ta.