# 05. 自定义 Hook
通过自定义 Hook,可以将组件逻辑提取到可重用的函数中。
目前为止,在 React 中有两种流行的方式来共享组件之间的状态逻辑: render props (opens new window) 和高阶组件 (opens new window),现在让我们来看看 Hook 是如何在让你不增加组件的情况下解决相同问题的。
# 提取自定义 Hook
自定义 Hook 是一个函数,其名称以 “use
” 开头,函数内部可以调用其他的 Hook。 例如,下面的 useFriendStatus
是我们第一个自定义的 Hook:
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;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
与 React 组件不同的是,自定义 Hook 不需要具有特殊的标识。我们可以自由的决定它的参数是什么,以及它应该返回什么(如果需要的话)。换句话说,它就像一个正常的函数。但是它的名字应该始终以 use
开头,这样可以一眼看出其符合 Hook 的规则 (opens new window)。
# 使用自定义 Hook
现在我们已经把这个逻辑提取到 useFriendStatus
的自定义 Hook 中,然后就可以使用它了:
function FriendStatus(props) {
const isOnline = useFriendStatus(props.friend.id);
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
2
3
4
5
6
7
function FriendListItem(props) {
const isOnline = useFriendStatus(props.friend.id);
return (
<li style={{ color: isOnline ? 'green' : 'black' }}>
{props.friend.name}
</li>
);
}
2
3
4
5
6
7
8
9
**这段代码等价于原来的示例代码吗?**等价,它的工作方式完全一样。如果你仔细观察,你会发现我们没有对其行为做任何的改变,我们只是将两个函数之间一些共同的代码提取到单独的函数中。自定义 Hook 是一种自然遵循 Hook 设计的约定,而并不是 React 的特性。
**自定义 Hook 必须以 “use
” 开头吗?**必须如此。这个约定非常重要。不遵循的话,由于无法判断某个函数是否包含对其内部 Hook 的调用,React 将无法自动检查你的 Hook 是否违反了 Hook 的规则 (opens new window)。
**在两个组件中使用相同的 Hook 会共享 state 吗?**不会。自定义 Hook 是一种重用状态逻辑的机制(例如设置为订阅并存储当前值),所以每次使用自定义 Hook 时,其中的所有 state 和副作用都是完全隔离的。
**自定义 Hook 如何获取独立的 state?**每次调用 Hook,它都会获取独立的 state。由于我们直接调用了 useFriendStatus
,从 React 的角度来看,我们的组件只是调用了 useState
和 useEffect
。 正如我们在之前章节 (opens new window)中了解到的 (opens new window)一样,我们可以在一个组件中多次调用 useState
和 useEffect
,它们是完全独立的。
# 小总结
- 自定义hook就像一个普通函数,内部包含可复用的组件逻辑。
- 函数名以
use
开头。 state
是独立的。