Xây dựng Hooks của bạn

https://vi.reactjs.org/docs/hooks-custom.html

Xây dựng Hooks 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.

Khi chúng tôi đang tìm hiểu về cách sử dụng Effect Hook, chúng tôi thấy thành phần này từ một ứng dụng trò chuyện hiển thị thông báo cho biết bạn bè đang trực tuyến hay ngoại tuyến:

import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });
  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

Bây giờ, giả sử ứng dụng trò chuyện của chúng tôi cũng có danh sách liên hệ và chúng tôi muốn hiển thị tên của những người dùng trực tuyến bằng màu xanh lục. Chúng tôi có thể sao chép và dán logic tương tự ở trên vào thành phần FriendListItem của mình nhưng nó sẽ không lý tưởng:

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

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

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

Thay vào đó, chúng tôi muốn chia sẻ logic này giữa FriendStatus và FriendListItem.

Theo truyền thống trong React, chúng tôi có hai cách phổ biến để chia sẻ logic trạng thái giữa các thành phần: kết xuất đạo cụ và các thành phần bậc cao hơn. Bây giờ chúng ta sẽ xem xét cách Hooks 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 hơn vào cây.

Trích xuất Hook tùy chỉnh

Khi chúng ta muốn chia sẻ logic giữa hai hàm JavaScript, chúng ta giải nén nó sang hàm thứ ba. Cả hai thành phần và Hook đều là các hàm, vì vậy điều này cũng phù hợp với chúng!

Hook tùy chỉnh là một hàm JavaScript có tên bắt đầu bằng "use" và có thể gọi các Hook khác. Ví dụ: useFriendStatus dưới đây là Hook tùy chỉnh đầu tiên của chúng tôi:

import { useState, useEffect } from 'react';

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

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

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

  return isOnline;
}

Không có gì mới bên trong nó - logic được sao chép từ các thành phần ở trên. Cũng giống như trong một thành phần, hãy đảm bảo chỉ gọi các Hook khác một cách vô điều kiện ở cấp cao nhất của Hook tùy chỉnh của bạn.

Không giống như một thành phần React, một Hook tùy chỉnh không cần phải có một chữ ký cụ thể. Chúng ta có thể quyết định những gì nó lấy làm đối số và những gì, nếu có, nó sẽ trả về. Nói cách khác, nó giống như một chức năng bình thường. Tên của nó phải luôn bắt đầu bằng cách sử dụng để bạn có thể biết ngay rằng các quy tắc của Hooks áp dụng cho nó.

Mục đích sử dụng của chúng tôiFriendStatus Hook là đăng ký cho chúng tôi trạng thái của một người bạn. Đây là lý do tại sao nó lấy friendID làm đối số và trả về liệu người bạn này có trực tuyến hay không:

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

  // ...

  return isOnline;
}

Using a Custom Hook

Lúc đầu, mục tiêu đã nêu của chúng tôi là xóa logic trùng lặp khỏi các thành phần FriendStatus và FriendListItem. Cả hai người họ đều muốn biết liệu một người bạn có trực tuyến hay không.

Bây giờ chúng ta đã trích xuất logic này thành hook useFriendStatus, chúng ta chỉ có thể sử dụng nó:

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

Mã này có tương đương với các ví dụ ban đầu không? Vâng, nó hoạt động theo cùng một cách. Nếu quan sát kỹ, bạn sẽ thấy chúng tôi không thực hiện bất kỳ thay đổi nào đối với hành vi. Tất cả những gì chúng tôi đã làm là trích xuất một số mã chung giữa hai hàm thành một hàm riêng biệt. Hooks tùy chỉnh là một quy ước tự nhiên tuân theo thiết kế của Hooks, chứ không phải là một tính năng React.

Tôi có phải đặt tên Hooks tùy chỉnh của mình bắt đầu bằng “use” không? Vui lòng làm. Quy ước này rất quan trọng. Nếu không có nó, chúng tôi sẽ không thể tự động kiểm tra xem có vi phạm các quy tắc của Hooks hay không vì chúng tôi không thể biết liệu một hàm nhất định có chứa các lệnh gọi đến Hooks bên trong nó hay không.

Hai thành phần sử dụng cùng trạng thái Hook có chia sẻ được không? Không. Móc tùy chỉnh là một cơ chế để sử dụng lại logic trạng thái (chẳng hạn như thiết lập đăng ký và ghi nhớ giá trị hiện tại), nhưng mỗi khi bạn sử dụng Móc tùy chỉnh, tất cả trạng thái và hiệu ứng bên trong nó đều bị cô lập hoàn toàn.

Làm thế nào để một Hook tùy chỉnh có trạng thái bị cô lập? Mỗi cuộc gọi đến một Hook sẽ có trạng thái bị cô lập. Bởi vì chúng tôi gọi trực tiếp useFriendStatus, theo quan điểm của React, thành phần của chúng tôi chỉ gọi useState và useEffect. Và như chúng ta đã học trước đó, chúng ta có thể gọi useState và useEffect nhiều lần trong một thành phần và chúng sẽ hoàn toàn độc lập.

Mẹo: Truyền thông tin giữa các móc Vì Hook là các hàm nên chúng ta có thể truyền thông tin giữa chúng.

Để minh họa điều này, chúng tôi sẽ sử dụng một thành phần khác từ ví dụ trò chuyện giả định của chúng tôi. Đây là bộ chọn người nhận tin nhắn trò chuyện hiển thị liệu người bạn hiện đang chọn có trực tuyến hay không:

const friendList = [
  { id: 1, name: 'Phoebe' },
  { id: 2, name: 'Rachel' },
  { id: 3, name: 'Ross' },
];

function ChatRecipientPicker() {
  const [recipientID, setRecipientID] = useState(1);
  const isRecipientOnline = useFriendStatus(recipientID);

  return (
    <>
      <Circle color={isRecipientOnline ? 'green' : 'red'} />
      <select
        value={recipientID}
        onChange={e => setRecipientID(Number(e.target.value))}
      >
        {friendList.map(friend => (
          <option key={friend.id} value={friend.id}>
            {friend.name}
          </option>
        ))}
      </select>
    </>
  );
}

Chúng tôi giữ ID bạn bè hiện được chọn trong biến trạng thái ID người nhận và cập nhật nó nếu người dùng chọn một người bạn khác trong bộ chọn .

Bởi vì lệnh gọi useState Hook cung cấp cho chúng ta giá trị mới nhất của biến trạng thái nhận ID, chúng ta có thể chuyển nó vào useFriendStatus Hook tùy chỉnh của mình dưới dạng đối số:

  const [recipientID, setRecipientID] = useState(1);
  const isRecipientOnline = useFriendStatus(recipientID);

Điều này cho chúng tôi biết liệu người bạn hiện được chọn có trực tuyến hay không. Nếu chúng tôi chọn một người bạn khác và cập nhật biến trạng thái ID người nhận, useFriendStatus Hook của chúng tôi sẽ hủy đăng ký khỏi người bạn đã chọn trước đó và đăng ký trạng thái của người mới được chọn.

useYourImagination()

Các móc tùy chỉnh mang đến sự linh hoạt trong việc chia sẻ logic mà trước đây không thể có trong các thành phần React. Bạn có thể viết các Hook tùy chỉnh bao gồm một loạt các trường hợp sử dụng như xử lý biểu mẫu, hoạt ảnh, đăng ký khai báo, bộ hẹn giờ và có thể nhiều thứ khác mà chúng tôi chưa xem xét. Hơn thế nữa, bạn có thể tạo Hook dễ dàng sử dụng như các tính năng tích hợp của React.

Cố gắng chống lại việc thêm trừu tượng quá sớm. Giờ đây, các thành phần chức năng có thể làm được nhiều việc hơn, có khả năng thành phần chức năng trung bình trong cơ sở mã của bạn sẽ dài hơn. Điều này là bình thường - đừng cảm thấy như bạn phải ngay lập tức chia nó thành Hooks. Nhưng chúng tôi cũng khuyến khích bạn bắt đầu phát hiện các trường hợp trong đó Hook tùy chỉnh có thể ẩn logic phức tạp đằng sau một giao diện đơn giản hoặc giúp gỡ rối một thành phần lộn xộn.

Ví dụ: có thể bạn có một thành phần phức tạp chứa nhiều trạng thái cục bộ được quản lý theo cách đặc biệt. useState không làm cho việc tập trung hóa logic cập nhật dễ dàng hơn, vì vậy bạn có thể thích viết nó như một trình giảm bớt Redux:

function todosReducer(state, action) {
  switch (action.type) {
    case 'add':
      return [...state, {
        text: action.text,
        completed: false
      }];
    // ... other actions ...
    default:
      return state;
  }
}

Các bộ giảm thiểu rất thuận tiện để kiểm tra một cách riêng lẻ và mở rộng quy mô để thể hiện logic cập nhật phức tạp. Bạn có thể chia nhỏ chúng thành các thanh giảm nhỏ hơn nếu cần. Tuy nhiên, bạn cũng có thể tận hưởng những lợi ích khi sử dụng trạng thái cục bộ của React hoặc có thể không muốn cài đặt một thư viện khác.

Vì vậy, điều gì sẽ xảy ra nếu chúng ta có thể viết useReducer Hook cho phép chúng ta quản lý trạng thái cục bộ của thành phần của chúng ta bằng một trình giảm thiểu? Một phiên bản đơn giản của nó có thể trông như thế này:

function useReducer(reducer, initialState) {
  const [state, setState] = useState(initialState);

  function dispatch(action) {
    const nextState = reducer(state, action);
    setState(nextState);
  }

  return [state, dispatch];
}

Bây giờ chúng ta có thể sử dụng nó trong thành phần của mình và để bộ giảm tốc thúc đẩy quản lý trạng thái của nó:

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

  function handleAddClick(text) {
    dispatch({ type: 'add', text });
  }

  // ...
}

Last updated

Was this helpful?