[官方中文文檔](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。