跳至主要内容

[ReactDoc] React Hooks 起步走

本文章內容均來自 React 官方文件:

為什麼需要用 React Hooks​

  • 過去的 React 很難在不同元件中重複使用固定的邏輯,因而使用 render props 或 higher-order components 的方式;透過 Hooks 將可以將固定的邏輯從元件中抽離出來,以進行獨立的測試和使用,同時不用改變原本元件間的階層關係。
  • 過去的 React 常常會在生命週期中混雜了許多帶有不同邏輯的方法;透過 Hooks 可以將一個 component 根據他們的關聯性拆成許多較小的函式,而不用被迫根據生命週期將它們拆開來。
  • 過去的 React 常使用 class 的想法,增加了新手在學習上的困難,同時 class 在打包的過程中無法被有效的優化;透過 Hooks 一樣可以在不使用 class 的情況下,使用 React 的各種功能。

❗ 在 class 中不能使用 hook。

使用 Hook 的原則 ‼️​

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​
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​

你很有可能要向伺服器請求資料(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)​

過去當我們想要在不同元件中使用相同的處理邏輯時,最常見的方式是透過 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

資料來源​