React: Concept: Hook: Reuse

 1st March 2021 at 9:49pm

复用 hook 是通过传递 state 变量:

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

function FriendStatus(props) {
  const isOnline = useFriendStatus(props.friend.id);

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

重点在于:

  • 即使 isOnline 是在 useFriendStatus 中被调用的,它实际上是存储在 FriendStatus 函数的实例中的。这意味着它在 FriendStatus 的实例中的值,跟另外一个使用 useFriendStatus 的函数式组件(如 FriendListItem)之间是不共享
  • 本质上,FriendStatus 调用 useFriendStatus 的过程,可以当作把后者的实现拷贝过来
  • useFriendStatus 的参数可以任意定,它需要什么就传什么;对于返回值,如果返回的内容有多个,可以用 object 包裹一下

同时,一个 hook 它的参数可以是另外一个 hook 的返回值,比如下面 useFriendStatus 使用的是 useState 的返回值。这意味着你可以把 state 中的变量作为参数传给 hook:

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

我做了个视频讲解: