[官方中文文档](https://zh-hans.reactjs.org/docs/hooks-intro.html)
## Hook 简介
*Hook*是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
```
import React, { useState } from 'react';
function Example() {
// 声明一个新的叫做 “count” 的 state 变量 const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
```
在我们继续之前,请记住 Hook 是:
* **完全可选的。**你无需重写任何已有代码就可以在一些组件中尝试 Hook。但是如果你不想,你不必现在就去学习或使用 Hook。
* **100% 向后兼容的。**Hook 不包含任何破坏性改动。
* **现在可用。**Hook 已发布于 v16.8.0。
Hook 为已知的 React 概念提供了更直接的 API:props, state,context,refs 以及生命周期。稍后我们将看到,Hook 还提供了一种更强大的方式来组合他们。
## State Hook
~~~
import React, { useState } from 'react';
function Example() {
// 声明一个叫 “count” 的 state 变量和一个让你更新它的函数 默认是0
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
~~~
**`useState`需要哪些参数?** `useState()`方法里面唯一的参数就是初始 state。
**`useState`方法的返回值是什么?** 返回值为:当前 state 以及更新 state 的函数。这就是我们写`const [count, setCount] = useState()`的原因。
### 读取 State
Hook 里 直接使用 变量名 count 就可以获取 state的值(class 中需要使用 this.state.count)
~~~
<p>You clicked {count} times</p>
~~~
### 更新 State
Hook 里,直接调用`setCount` 赋值 (class 里 `this.setState()`来更新`count`值)
~~~
<button onClick={() => setCount(count + 1)}> Click me
</button>
~~~
## Effect Hook
在 React 组件中执行过数据获取、订阅或者手动修改过 DOM。我们统一把这些操作称为“副作用”,或者简称为“作用”。
`useEffect`就是一个 Effect Hook,给函数组件增加了操作副作用的能力。它跟 class 组件中的`componentDidMount`、`componentDidUpdate`和`componentWillUnmount`具有相同的用途,只不过被合并成了一个 API。
~~~
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// useEffect 相当于 componentDidMount 和 componentDidUpdate:
// 组件加载以后执行
useEffect(() => {
// 使用浏览器的 API 更新页面标题
document.title = `You clicked ${count} times`;
});
// 这个是空数组, 只会在组件加载进入 DOM 后执行一次
useEffect(() => {
// 使用浏览器的 API 更新页面标题
document.title = `You clicked ${count} times`;
}, []);
// 这个在每次count 变量改变都会触发, 相当于vue 里的 watch 了 count变量
useEffect(() => {
// 使用浏览器的 API 更新页面标题
document.title = `You clicked ${count} times`;
}, [count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
~~~
当你调用`useEffect`时,就是在告诉 React 在完成对 DOM 的更改后运行你的“副作用”函数。由于副作用函数是在组件内声明的,所以它们可以访问到组件的 props 和 state。默认情况下,React 会在每次渲染后调用副作用函数 ——**包括**第一次渲染的时候。
跟`useState`一样,你可以在组件中多次使用`useEffect`
### 返回值
**effect可以返回一个函数** 这是 effect 可选的清除机制, 每个 effect 都可以返回一个清除函数。React 会在**组件卸载**的时候执行清除操作。这就是为什么 React*会*在执行当前 effect 之前对上一个 effect 进行清除。
## 使用 React Hooks 的规则
Hook 就是 JavaScript 函数,但是使用它们会有两个额外的规则:
* **只在最顶层调用 Hook** :**不要在循环,条件或嵌套函数中调用 Hook**,确保总是在你的 React 函数的最顶层以及任何 **return **之前调用他们。
* **只在 React 函数中调用 Hook**:**不要在普通的 JavaScript 函数中调用 Hook**。你可以:
* ✅ 在 React 的函数组件中调用 Hook
* ✅ 在自定义 Hook 中调用其他 Hook
遵循此规则,确保组件的状态逻辑在代码中清晰可见。
## 自定义 Hook
Hook 来订阅一个好友的在线状态。假设我们想在另一个组件里重用这个订阅逻辑。
首先,我们把这个逻辑抽取到一个叫做`useFriendStatus`的自定义 Hook 里:
~~~
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;
}
~~~
它将`friendID`作为参数,并返回该好友是否在线:
现在我们可以在两个组件中使用它:
~~~
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>
);
}
~~~
每个组件间的 state 是完全独立的。Hook 是一种复用*状态逻辑*的方式,它不复用 state 本身。事实上 Hook 的每次*调用*都有一个完全独立的 state —— 因此你可以在单个组件中多次调用同一个自定义 Hook。