PJCHENder 未整理筆記

[ReactDoc] React Hooks 起步走

2019-03-13

[ReactDoc] React Hooks 起步走

@(React)[react docs, React, React Hook]

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

為什麼需要用 React Hooks

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

:exclamation: 在 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 會在每次重新轉譯的時候保留這個 stateuseState 這個函式會回傳兩個東西,分別是「當前的 state(current state)」和一個可以「更新此 state 的方法」;這個方法所帶入的參數就是「state 的預設值(initial state)」,只有在元件初次轉譯時會使用到它。

過去在撰寫 class 時,很習慣會把所有的 state 都包在一個物件中,但在 Hooks 時因為不像 this.setState 會把物件進行 merge 的動作,而是**整個物件置換(replace)掉,因此官方建議「可以使用多個 useState,然後把經常會一起變動資料放在一個 state 中」**去變更。

:exclamation: 要特別留意的是,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, componentDidUpdatecomponentWillUnmount,但統整成單一個 API。

useEffect 會在 React 更新完 DOM 之後執行這個 “effect” ,透過將 effect 定義在 function component 內將可以使用到 props 和 state 。

React 會在每次頁面重新轉譯後執行 effects,包含第一次畫面轉譯後。

在這個範例中,會在 React 更新完 DOM 後設定 document 的 title 屬性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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 前被執行

1
2
3
4
5
6
7
8
9
10
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 componentsrender props,現在透過自訂的 Hooks(Custom Hooks)可以讓你做到這件事,而且不用改變原本元件間的階層關係。

每一個使用 Hooks 的元件內,其 state 都是各自獨立的,Hooks 是一種重複使用「靜態邏輯(stateful logic)」的方式,而不是重複使用「狀態(state)」,也就是說,在每一次呼叫 Hook 時,都是完全獨立的狀態,因此,你甚至可以在一個元件內重複使用自訂的 Hook。

撰寫自訂的 hooks(Custom Hooks)就和寫一個 JavaScript 函式一樣,唯一的慣例是以 use 作為函式名稱的開頭,如此 lint 工具將可以辨識到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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:

1
2
3
4
5
6
7
8
function FriendStatus(props) {
const isOnline = useFriendStatus(props.friend.id);

if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}

其他 Hooks

安裝 ESLint Plugin

資料來源

掃描二維條碼,分享此文章