[ReactDoc] React Hooks 起步走
本文章內容均來自 React 官方文件:
- Introducing Hooks @ React Docs
- Hooks at a Glance @ React Docs
為什麼需要用 React Hooks
- 過去的 React 很難在不同元件中重複使用固定的邏輯,因而使用
render props
或higher-order components
的方式;透過 Hooks 將可以將固定的邏輯從元件中抽離出來,以進行獨立的測試和使用,同時不用改變原本元件間的階層關係。 - 過去的 React 常常會在生命週期中混雜了許多帶有不同邏輯的方法;透過 Hooks 可以將一個 component 根據他們的關聯性拆成許多較小的函式,而不用被迫根據生命週期將它們拆開來。
- 過去的 React 常使用 class 的想法,增加了新手在學習上的困難,同時 class 在打包的過程中無法被有效的優化;透過 Hooks 一樣可以在不使用 class 的情況下,使用 React 的各種功能。
❗ 在 class 中不能使用 hook。
使用 Hook 的原則 ‼️
- Hooks at a Glance: Rules of Hook @ React Docs
- Rules of Hooks @ React Docs
Hooks 就是單純的 JavaScript 函式,但有兩個額外的附加規則:
- 只能在最高層(at the top level)呼叫 Hooks,千萬不能在條件式(conditions)、迴圈(loops)或嵌套函式(nested functions)中呼叫 Hook。
- 除了自己客製化的 Hooks 之外,只能在 React 的 function components 中呼叫 Hook,不能在一般的 JavaScript 函式中呼叫 Hooks。
之所以不能在條件式、迴圈中使用 Hooks ,是因為 React 會依賴這些 Hooks 呼叫到的順序,當我們將 Hooks 放到條件式或迴圈時,就會破壞了這些 Hooks 被互叫到的順序,如此會造成錯誤。因此如果有需要用到條件判斷,只需要把判斷式放在 Hook 內就可以了。
初探 Hooks(Hooks at a Glance)
State Hook
keywords: useState
- Hooks at a Glance: State Hook @ React Docs
- Using the State Hook @ React Docs
- Should I use one or many state variables? @ React Docs - Hooks FAQ
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
這裡 useState
就是一個 Hook,React 會在每次重新轉譯的時候保留這個 state。useState
這個函式會回傳兩個東西,分別是「當前的 state(current state)」和一個可以「更新此 state 的方法」;這個方法所帶入的參數就是「state 的預設值(initial state)」,只有在元件初次轉譯時會使用到它。
過去在撰寫 class 時,很習慣會把所有的 state 都包在一個物件中,但在 Hooks 時因為不像 this.setState
會把物件進行 merge 的動作,而是**整個物件置換(replace)掉,因此官方建議「可以使用多個 useState
,然後把經常會一起變動資料放在一個 state 中」**去變更。
❗ 要特別留意的是,
useState
裡面的 state 在使用物件,並要呼叫setSomething
時,和 class 中的this.setState
不同,每次使用setSomething
是把整個物件換掉(replace),而不像this.setState
是去 merge 物件。 參考:Should I use one or many state variables? @ React Docs - Hooks FAQ
Effect Hook
keywords: useEffect
- Hooks at a Glance: Effect Hook @ React Docs
- Using the Effect Hook @ React Docs
你很有可能要向伺服器請求資料(data fetching)、訂閱(subscription)、手動操作 DOM 等等,這些操作被稱作是有「副作用(side effects)」,或簡稱為 effects,因為這些操作可能會影響到其他元件,並且無法在元件轉譯期間被完成。
透過 useEffect
這個 Hook,可以在 function component 中執行帶有副作用的方法,它就如同 React classes 中使用的 componentDidMount
, componentDidUpdate
和 componentWillUnmount
,但統整成單一個 API。
useEffect
會在 React 更新完 DOM 之後執行這個 "effect" ,透過將 effect 定義在 function component 內將可以使用到 props 和 state 。
React 會在每次頁面重新轉譯後執行 effects,包含第一次畫面轉譯後。
在這個範例中,會在 React 更新完 DOM 後設定 document 的 title 屬性:
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// 類似於 componentDidMount 和 componentDidUpdate
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
// 如果需要做一些清除的動作
// 這個函式會在 component unmount 和 before re-running the effect 前被執行
return () => {
// clean up ...
};
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
在 effects 中若有需要的話,可以選擇性的回傳一個「清除用的(clean up)」函式,通常用來解除註冊某些事件,這個函式會在 component unmount 和 before re-running the effect 前被執行:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
// 如果需要做一些清除的動作
// 這個函式會在 component unmount 和 before re-running the effect 前被執行
return () => {
// clean up ...
};
});
自訂 Hooks(Building Your Own Hooks)
- Hooks at a Glance: Building Your Own Hooks @ React Docs
- Building Your Own Hooks @ React Docs
過去當我們想要在不同元件中使用相同的處理邏輯時,最常見的方式是透過 higher-order components 和 render props,現在透過自訂的 Hooks(Custom Hooks)可以讓你做到這件事,而且不用改變原本元件間的階層關係。
每一個使用 Hooks 的元件內,其 state 都是各自獨立的,Hooks 是一種重複使用「靜態邏輯(stateful logic)」的方式,而不是重複使用「狀態(state)」,也就是說,在每一次呼叫 Hook 時,都是完全獨立的狀態,因此,你甚至可以在一個元件內重複使用自訂的 Hook。
撰寫自訂的 hooks(Custom Hooks)就和寫一個 JavaScript 函式一樣,唯一的慣例是以 use
作為函式名稱的開頭,如此 lint 工具將可以辨識到:
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;
}
直接就可以使用這個 Custom Hooks:
function FriendStatus(props) {
const isOnline = useFriendStatus(props.friend.id);
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
其他 Hooks
安裝 ESLint Plugin
- Hook of Rules @ React Docs
- [React CLI](/Users/pjchen/Projects/Notes/source/_posts/React/[React] react-cli, create-react-app.md#ESLint) @ PJCHENder Notes
資料來源
-
Introducing Hooks @ React Docs
-
Hooks at a Glance @ React Docs
-
The Guide to Learning React Hooks (Examples & Tutorials) @ KendoReact