1. 程式人生 > >React v16.7 "Hooks"

React v16.7 "Hooks"

Hooks 目前已經在 v16.7.0 alpha 版本,如果想了解更多的關於這個功能的介紹和討論,可以移步文章底部的 Proposal discussion 和文件連結。

什麼是 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

JavaScript
123456789101112131415 import{useState}from'react';functionExample(){// Declare a new state variable, which we'll call "count"const[count,setCount]=useState(0);return(<div><p>Youclicked{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:

12345678 functionExampleWithManyStates(){// 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

JavaScript
1234567891011121314151617181920 import{useState,useEffect}from'react';functionExample(){const[count,setCount]=useState(0);// Similar to componentDidMount and componentDidUpdate:useEffect(()=>{// Update the document title using the browser APIdocument.title=`Youclicked${count}times`;});return(<div><p>Youclicked{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)。但是限於篇幅,很多功能強大的部分和一些注意事項在這篇文章裡並沒有過多講解,請大家移步官方文件學習更詳細的姿勢(牆裂推薦)。

歡迎大家在評論區討論指正。