× Giới thiệu Lịch khai giảng Tin tức Sản phẩm học viên

Tìm hiểu Custom Hooks trong React

25/12/2023 01:22

Hook tùy chỉnh cho phép bạn dễ dàng sử dụng lại logic trạng thái giữa các thành phần. Tìm hiểu Custom Hooks trong React ngay trong bài viết sau

Hook tùy chỉnh cho phép bạn dễ dàng sử dụng lại logic trạng thái giữa các thành phần.

Hình ảnh có thể viết một hàm chỉ bằng mã logic thành phần của bạn nhưng không có giao diện người dùng (JSX) và sau đó sử dụng lại hàm đó trong một số thành phần...đó là chức năng của hook tùy chỉnh.

hook là một bổ sung mới trongReact 16.8

Background

  • Trước hook, React có các mẫu để sử dụng lại logic trạng thái nhưng không có cách tiếp cận nào hoạt động tốt
    • Đầu tiên, các thành phần bậc cao hơn
    • Sau đó, kết xuất đạo cụ
  • Sau hook, việc tạo hook tùy chỉnh là giải pháp lý tưởng để sử dụng lại logic trạng thái

Tại sao lại là Hook ?

Thật khó để sử dụng lại logic trạng thái giữa các thành phần.

  • hành vi có thể tái sử dụng
  • các mẫu hiện tại: kết xuất đạo cụ và các thành phần bậc cao hơn
  • cả hai mẫu đều tạo ra "địa ngục bao bọc" nơi các thành phần được bao quanh bởi các nhà cung cấp, người tiêu dùng, các thành phần bậc cao hơn, đạo cụ kết xuất, v.v...
  • hook cho phép bạn sử dụng lại logic trạng thái mà không thay đổi hệ thống phân cấp thành phần của bạn

Thực hành tốt nhất

hook tùy chỉnh hiện được coi là cách thực hành tốt nhất trong cộng đồng React.

Ưu tiên tạo hooklogic để có thể sử dụng lại thay vì mẫu render propshoặc high-order componentsnếu có thể.

Sử dụng ở đâu

Việc xây dựng Hook của riêng bạn cho phép bạn trích xuất logic thành phần thành các hàm có thể sử dụng lại.

Trong các lớp học Có hook
Thành phần bậc cao hơn, Đạo cụ kết xuất hook tùy chỉnh


Theo truyền thống trong React, chúng ta có hai cách phổ biến để chia sẻ logic trạng thái giữa các thành phần: render props và các thành phần bậc cao hơn. hook giải quyết nhiều vấn đề tương tự mà không buộc bạn phải thêm nhiều thành phần vào cây.

Bản trình diễn

Bắt đầu: Ví dụ về thành phần hàm với đối tượng

const baseUrl = 'http://localhost:3000';
const url = `${baseUrl}/photos?_page=1&_limit=10`;

function translateStatusToErrorMessage(status) {
  switch (status) {
    case 401:
      return 'Please login again.';
    case 403:
      return 'You do not have permission to view the photos.';
    default:
      return 'There was an error retrieving the photos. Please try again.';
  }
}

function checkStatus(response) {
  if (response.ok) {
    return response;
  } else {
    const httpErrorInfo = {
      status: response.status,
      statusText: response.statusText,
      url: response.url,
    };
    console.log(
      `logging http details for debugging: ${JSON.stringify(httpErrorInfo)}`
    );

    let errorMessage = translateStatusToErrorMessage(httpErrorInfo.status);
    throw new Error(errorMessage);
  }
}

function parseJSON(response) {
  return response.json();
}

function delay(ms) {
  return function (x) {
    return new Promise((resolve) => setTimeout(() => resolve(x), ms));
  };
}

const photoAPI = {
  getAll(page = 1, limit = 100) {
    return (
      fetch(`${url}?_page=${page}&_limit=${limit}`)
        // .then(delay(600))
        .then(checkStatus)
        .then(parseJSON)
        .catch((error) => {
          let errorMessage = translateStatusToErrorMessage(error);
          throw new Error(errorMessage);
        })
    );
  },
};

function PhotoList() {
  const [loading, setLoading] = React.useState(false);
  const [photos, setPhotos] = React.useState([]);
  const [error, setError] = React.useState(null);

  React.useEffect(() => {
    setLoading(true);
    setError(null);

    photoAPI
      .getAll(1)
      .then((data) => {
        setPhotos(data);
        setLoading(false);
      })
      .catch((error) => {
        setError(error.message);
        setLoading(false);
      });
  }, []);

  if (error) {
    return <div>{error}</div>;
  } else if (loading) {
    return <div>Loading...</div>;
  } else {
    return (
      <ul>
        {photos.map((photo) => {
          return (
            <li key={photo.id}>
              <img src={photo.thumbnailUrl} alt={photo.title} />
              <h3>{photo.title}</h3>
            </li>
          );
        })}
      </ul>
    );
  }
}

ReactDOM.createRoot(document.getElementById('root')).render(<PhotoList />);
 

Hoàn thành: Ví dụ về Thành phần hàm với Đối tượng API & Hook

const baseUrl = 'http://localhost:3000';
const url = `${baseUrl}/photos?_page=1&_limit=10`;

function translateStatusToErrorMessage(status) {
  switch (status) {
    case 401:
      return 'Please login again.';
    case 403:
      return 'You do not have permission to view the photos.';
    default:
      return 'There was an error retrieving the photos. Please try again.';
  }
}

function checkStatus(response) {
  if (response.ok) {
    return response;
  } else {
    const httpErrorInfo = {
      status: response.status,
      statusText: response.statusText,
      url: response.url,
    };
    console.log(
      `logging http details for debugging: ${JSON.stringify(httpErrorInfo)}`
    );

    let errorMessage = translateStatusToErrorMessage(httpErrorInfo.status);
    throw new Error(errorMessage);
  }
}

function parseJSON(response) {
  return response.json();
}

function delay(ms) {
  return function (x) {
    return new Promise((resolve) => setTimeout(() => resolve(x), ms));
  };
}

const photoAPI = {
  getAll(page = 1, limit = 100) {
    return (
      fetch(`${url}?_page=${page}&_limit=${limit}`)
        // .then(delay(600))
        .then(checkStatus)
        .then(parseJSON)
        .catch((error) => {
          let errorMessage = translateStatusToErrorMessage(error);
          throw new Error(errorMessage);
        })
    );
  },
};

function usePhotos() {
  const [loading, setLoading] = React.useState(false);
  const [photos, setPhotos] = React.useState([]);
  const [error, setError] = React.useState(null);

  React.useEffect(() => {
    setLoading(true);
    setError(null);

    photoAPI
      .getAll(1)
      .then((data) => {
        setPhotos(data);
        setLoading(false);
      })
      .catch((error) => {
        setError(error.message);
        setLoading(false);
      });
  }, []);

  return { loading, photos, error };
}

function PhotoList() {
  const { loading, photos, error } = usePhotos();

  if (error) {
    return <div>{error}</div>;
  } else if (loading) {
    return <div>Loading...</div>;
  } else {
    return (
      <ul>
        {photos.map((photo) => {
          return (
            <li key={photo.id}>
              <img src={photo.thumbnailUrl} alt={photo.title} />
              <h3>{photo.title}</h3>
            </li>
          );
        })}
      </ul>
    );
  }
}

ReactDOM.createRoot(document.getElementById('root')).render(<PhotoList />);
 

Quy tắc hook

  • Chỉ gọi hook ở cấp cao nhất (của thành phần chức năng của bạn)
    • đừng gọi chúng bên trong các vòng lặp (for), điều kiện (if) hoặc các hàm lồng nhau (chỉ bên trong phần thân thành phần hàm chính của bạn)
  • Chỉ gọi hook từ React Functions
    • hook gọi từ các thành phần hàm React
    • hook gọi từ các hook tùy chỉnh khác

Source: https://handsonreact.com/docs/custom-hooks