React v16.7 "Hooks" - What to Expect

什麼是 Hooks?

Hooks 是一個 React 函數組件內一類特殊的函數(通常以 “use” 開頭,比如 “useState”),使開發者能夠在 function component 裏依舊使用 state 和 life-cycles,以及使用 custom hook 複用業務邏輯。

動機

在 React 裏,function component 就是一個 pure render component,沒有 state 和 component life-cycle。如果需要這兩個中的任意一個,就需要變成 class component。在既有的 React API 下,這個模式有如下一些缺點:

組件間交流的耦合度很高,組件樹臃腫

在既有的模式下,React 的組件間通信無非是兩種,一種是單項數據流,另一種是通過 redux 之類的 global store 來實現全局狀態和各組件間的解耦。當有些狀態不適合放在 global store 的情況下,組件間邏輯的複用和溝通就變得十分困難(必須一層一層往下傳)。這一點在 Higher order component (高階組件) 和 render props 中尤其常見。我們爲了複用一些邏輯,單獨創造了很多 HOC(高階組件) 來向下傳遞狀態。這導致的問題就是當我們的應用規模變得越來越大的時候,一些無關 UI 的 wrapper 組件越來越多,React 組件樹變得越來越臃腫(在 devtool 中可以甚至看到數十層 wrapper)。某些業務場景下,一個 Tooltip component 裏面都嵌套了三四層額外的組件,使開發和調試的效率變得很低。

在新的 React hook 中,我們可以創建 custom hook,在其中複用一些邏輯,這些邏輯不再出現在組件樹中,而是成爲一個單獨的,獨立的邏輯單元,但是他們仍然響應 React 在渲染之間的變化。

JavaScript 的 class 產生的諸多疑惑

這一點是相對於 JavaScript 來說的。還記得剛入門 JavaScript 的時候,需要跨越的一個重要難關就是 ”this” 指向,以及原型鏈,繼承這些問題。即使我們真正覺得明白了其中的原理,在日常的開發中也難免因爲疏忽而踩坑,這一系列的問題導致新手相對比較難上手 React。舉個簡單的例子,React 組件內的 event listener 之前需要手動 bind this 的問題,這個問題就很難對一個 JavaScript 入門的新手解釋明白。

而這一系列的問題,將在 Hook 中被極大地解決。如果沒有 class,沒有了 this,可能上述的種種問題都不再是問題了。

Write Hooks

說了這麼多,Hooks API 是什麼樣呢?首先需要聲明的是,Hooks 是向後兼容的,class component 不會被移除。作爲開發者,可以慢慢遷移到這個新的 API。

Hooks 主要分爲三種:

 ●  State hooks (在 function component 中使用 state)
 ●  Effect hooks (在 function component 中使用生命週期和 side effect)
 ●  Custom hooks (自定義 hooks 用來複用組件邏輯,解決了上述的第一個動機中闡述的問題,這一部分就不在此多費篇幅介紹了,請大家移步文檔)。
State hooks
import { 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>
 
);
}

之前講過 hook 本質是一個特殊的函數(通常以 “use” 開頭)。在這裏,”useState” 就是一個 hook,通過它我們能夠嵌入組件內部的 state。這個函數返回一個 pair,第一個值是當前對應這個 hook 的 state 值,第二個是怎樣更新這個值。

我們可以從中感覺到,這兩個返回值分別對應的以前的用法是:

 ●  this.state
 ●  this.setState

除此之外,我們還可以在一個函數組件中使用多個 useState:


function ExampleWithManyStates() {
 
// Declare multiple state variables!
 
const [age, setAge] = useState(42);
 
const [fruit, setFruit] = useState('banana');
 
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
 
// ...
}

這給我們的一個非常大的好處就是我們能夠避免組件的 state 結構過於臃腫(因爲之前每個 component class 只能有一個 state),能夠獨立處理每個 state。另一個好處就是這種寫法非常直觀,一眼就可以看出和這個 state 相關的兩個變量,比如 [age, setAge]。

Effect hooks
import { useState, useEffect } from 'react';
function Example() {
 
const [count, setCount] = useState(0);
 
// Similar to componentDidMount and componentDidUpdate:
 useEffect
(() => {
   
// Update the document title using the browser API
   document
.title = `You clicked ${count} times`;
 
});
 
return (
   
<div>
     
<p>You clicked {count} times</p>
     
<button onClick={() => setCount(count + 1)}>
       
Click me
     
</button>
   
</div>
 
);
}

我們還需要解決一個問題,那就是怎樣在 function component 裏使用 life-cycles,生命週期函數。在這裏,所有的 life-cycles,比如 componentDidMount, componentDidUpdate, shouldUpdate, 等等都集合成一個 Hook,叫做 useEffect。這個函數類似 redux 中的 subscribe,每當 React 因爲 state 或是 props 而重新 render 的之後,就會觸發 useEffect 裏的這個 callback listener(在第一次 render 和每次 update 後觸發)。

爲什麼叫 useEffect 呢?因爲我們通常在生命週期內做的操作很多都會產生一些 side-effect(副作用)的操作,比如更新 DOM,fetch 數據,等等。

Other Built-in Hooks

除了 useState, useEffect 還有另外一些 React 自帶的 hooks。比如:

 ●  useContext

替代了 <Context.Consumer> 使用 render props 的寫法,使組件樹更加簡潔。

 ●  useReducer

相當於組件自帶的 redux reducer,負責接收 dispatch 分發的 action 並更新 state。

詳細用法請看文檔。

總結

讀到這裏,你可能理解了爲什麼這個新的 API 被叫做 “Hooks” 了。”Hooks” 本意是”鉤子“的意思。在 React 裏,hooks 就是一系列特殊的函數,使函數組件 (functional component) 內部能夠”鉤住“ React 內部的 state 和 life-cycles。

這個向後兼容的 API 在解決了一些既有問題的情況下,不僅使我們能夠更好地使用 state 和 life-cycles,真正功能強大的地方是使我們能夠更輕鬆地複用組件邏輯(custom hooks)。但是限於篇幅,很多功能強大的部分和一些注意事項在這篇文章裏並沒有過多講解,請大家移步官方文檔學習更詳細的姿勢(牆裂推薦)。


原文發佈時間爲:2018-11-02

本文作者:cyan

本文來自雲棲社區合作伙伴“前端大學”,瞭解相關信息可以關注“前端大學”。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章