[譯] React 將引入 Hooks,你怎麼看?
譯者|姚佳靈、無明
整理|覃雲
今天,在 2018 ReactConf 大會上,React 官方宣佈 React v16.7.0-alpha 將引入 Hooks,乍一看,你可能在想 Hooks 是什麼?有什麼用?且看下文分析。
Hooks 是什麼?
Hooks 是一種函式,該函式允許你“勾住(hook into)”React 狀態和來自函式元件的生命週期功能。Hook 在類內部不起作用,它們允許你無需類就使用 React。(不建議你馬上開始重寫你現有的元件,但你可以在新元件中開始使用 Hook。)
React 提供了一些內建 Hook,如 useState,你也可以建立自定義 Hooks 以在不同的元件中複用有狀態行為。
根據 React 官方給出的文件,Hooks 主要分為以下幾種:
-
State Hooks
-
Effect Hooks
-
自定義 Hooks
State Hook
該示例顯示了一個計數器,點選該按鈕,值會遞增。
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> ); }
在這裡,useState 是一個 Hook,我們在函式元件中呼叫它以給它新增一些本地狀態。React 將在重新渲染之間保留這個狀態。useState 返回一對值:currentstate 值和允許你更新它的函式。
可以從事件處理程式或其他位置呼叫該函式,這與類中的 this.setState 類似,除了其不會把舊的和新的狀態合併在一起。
宣告多個狀態變數
可以在單個元件中多次使用 State Hook:
function ExampleWithManyStates() { // Declare multiple state variables! const [age, setAge] = useState(42); const [fruit, setFruit] = useState('banana'); const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]); // ... }
陣列解構語法允許我們給狀態變數取不同的名字,這些變數是我們通過呼叫 useState 宣告的,這不是 useState API 的一部分。相反,React 假設如果多次呼叫 useState,那麼在每次渲染的時候要遵循相同的順序來做。
Effect Hook
你之前很可能已經執行了資料提取、訂閱、或手工改變來自 React 元件的 DOM。我們稱這些操作為“副作用(side effect)”,因為它們會影響其他元件,並且在渲染過程中無法完成。
Effect Hook、useEffect,增加了從函式元件執行副作用的功能。它與 React 類中的 componentDidMount、componentDidUpdate、和 componentWillUnmount 有相同的功能,但是統一為單個 API。(我們將展示一個例子,該示例在 Using the Effect Hook 中對 useEffect 和這些方法進行比較。)
例如,此元件在 React 更新 DOM 後設置文件標題:
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> ); }
當你呼叫 useEffect 時,你通知 React 在重新整理對 DOM 的更改後執行你的“effect”函式。Effect 在元件內宣告,因此可以訪問其 props 和 state。預設情況下,React 在每次渲染後執行 effect,包括第一次渲染。
效果還可以通過返回一個函式來指定它們之後如何“清理”。例如,此元件使用 effect 來訂閱朋友的線上狀態,並通過取消訂閱來清理:
import { useState, useEffect } from 'react'; function FriendStatus(props) { const [isOnline, setIsOnline] = useState(null); function handleStatusChange(status) { setIsOnline(status.isOnline); } useEffect(() => { ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline'; }
在此示例中,當元件解除安裝時,以及在由於後續渲染而重新執行 effect 之前,React 將取消訂閱我們的 ChatAPI。(如果我們傳遞給 ChatAPI 的 props.friend.id 沒有變化,有辦法讓 React 跳過重新訂閱。)就像使用 useState 一樣,可以在元件中使用多個 effect:
function FriendStatusWithCounter(props) { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); const [isOnline, setIsOnline] = useState(null); useEffect(() => { ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); function handleStatusChange(status) { setIsOnline(status.isOnline); } // ...
Hook 允許你通過部件的關聯情況(例如新增和刪除訂閱)來組織元件中的副作用,而不是基於生命週期方法強制拆分。
自定義 Hooks
自定義 Hooks 主要用來複用元件邏輯,詳情請閱讀:
https://reactjs.org/docs/hooks-overview.html
Hook 的規則
Hook 是 JavaScript 函式,但強加了兩個額外的規則:
-
只能在頂層呼叫 Hook,不要在迴圈、條件或巢狀函式中呼叫 Hook。
-
僅從 React 功能元件呼叫 Hook。不要從常規 JavaScript 函式呼叫 Hook。(還有另一個有效的地方來呼叫 Hook,即你的自定義 Hook。)
這裡( https://www.npmjs.com/package/eslint-plugin-react-hooks )提供了一個 linter 外掛來自動執行這些規則,這些規則乍看起來有些令人疑惑,但它們對 Hook 的良好執行至關重要。
為什麼要在 React 中引入 Hooks?
至於為何要在 React 中引入 Hooks,React 官方給出了答案:
Hooks 解決了 React 中各種看似不相關的問題,這些問題是我們在開發和維護數以萬計的元件時遇到的。無論你是在學習 React,還是每天在使用它,還是選擇使用具有類似元件模型的其他庫,你都可能會發現這類問題。
難以在元件之間重用有狀態邏輯
React 沒有提供可將可重用行為“附加”到元件的方法。如果你用過 React 一段時間,可能會對渲染 prop 和高階元件等模式比較熟悉,它們試圖解決這個問題。但是這些模式要求你在使用它們時重構元件,這可能很麻煩,並且程式碼會變得難以維護。
如果你看一下 React DevTools 中的典型 React 應用程式,你可能會發現“包裝器地獄”,元件被層層的提供者、消費者、高階元件、渲染 prop 和其他抽象元件組包圍。雖然我們可以在 DevTools 中過濾掉它們,但這反應了一個更深層次的問題:React 需要一個更好的原語來共享有狀態邏輯。
使用 Hooks,你可以從元件中提取有狀態邏輯,可以進行獨立測試和重用。Hooks 允許你在不更改元件層次結構的情況下重用有狀態邏輯。這樣可以輕鬆地在多個元件之間或與社群共享 Hooks。
複雜的元件變得難以理解
我們通常維護的元件都先從簡單的開始,然後逐漸加入無法管理的狀態邏輯和副作用。每個生命週期方法通常包含不相關邏輯的混合。例如,元件可能會在 componentDidMount 和 componentDidUpdate 時獲取資料。不過,相同的 componentDidMount 方法可能還包含一些設定事件監聽器的無關邏輯,並在 componentWillUnmount 中執行清理工作。一起更改的相互關聯的程式碼會被拆分,但完全不相關的程式碼最終會組合在一個方法中。這樣就很容易引入錯誤和導致不一致。
在很多情況下,很難將這些元件分解為更小的元件,因為狀態邏輯到處都是。測試它們也很困難。這是很多人更喜歡將 React 與單獨的狀態管理庫相結合的原因之一。不過,這通常會引入太多的抽象,要求你在不同的檔案之間跳轉,讓重用元件變得更加困難。
為了解決這個問題,Hooks 讓你可以根據相關的部分將一個元件拆分為較小的函式,而不是基於生命週期方法進行強制拆分。你還可以選擇使用 reducer 來管理元件的本地狀態,以使其更具可預測性。
類讓人和機器感到困惑
根據我們的觀察,類是學習 React 最大的障礙。你必須瞭解 JavaScript 中的 this 是怎麼回事,這與它在大多數語言中的使用方法有很大不同。你必須記住繫結事件處理程式。人們可以很好地理解 prop、狀態和自上而下的資料流,但對類的使用仍然感到很掙扎。React 中的函式和類元件之間的區別以及何時使用哪個元件在經驗豐富的 React 開發人員之間也存在分歧。
另外,React 已經推出了大約五年時間,Facebook 希望 React 在未來五年充滿活力。正如 Svelte、Angular、Glimmer 和其他專案所表明的那樣,預編譯元件具有很大的潛力。
最近,他們一直在嘗試使用 Prepack(https://prepack.io/)進行元件摺疊,我們已經看到了充滿希望的結果。不過,他們發現類元件的無意識模式可能會使這些優化回退。在類上應用現今的一些工具時也存在一些問題。例如,類難以進行 minify,並且類會導致熱過載變得不可靠。他們希望提供一種 API,使程式碼更有可能保持在可優化的路徑上。
為了解決這些問題,Hooks 讓你可以在不使用類的情況下使用更多的 React 特性。從概念上講,React 元件很接近於函式。Hooks 擁抱函式,但不會以犧牲 React 精神為代價。Hooks 提供了命令式的程式設計方式,不需要你學習複雜的函式式或反應式程式設計技術。
例子: https://reactjs.org/docs/hooks-overview.html
逐步採用策略
我們知道,React 開發人員專注於產品,他們沒有時間研究釋出的每個新的 API。Hooks 是新東西,在學習或採用它們之前先等待更多的示例和教程,這樣可能會更好。
我們也知道為 React 新增新原語的標準是非常高的。對於好奇的讀者,這裡準備了一個詳細的 RFC(https://github.com/reactjs/rfcs/pull/68),其中包含更多的細節,併為特定設計決策和現有相關技術提供了額外的視角。
關鍵的是,Hooks 可以與現有程式碼同時存在,因此你可以逐步採用它們。React 官方正在分享這個實驗性的 API,以便從社群中那些有興趣打造 React 未來的人那裡獲得早期反饋,將公開進行 Hooks 的迭代。
最後,沒有必要急於使用 Hooks。建議避免任何“重大的重寫”,特別是對於複雜的現有類元件來說。進入“Hooks 思考”模式需要精神上的轉變。根據經驗,最好先在非關鍵的新元件中練習使用 Hooks,並確保團隊中的每個人都能適應。
React 官方打算讓 Hooks 涵蓋所有與類相關的用例,但在可預見的未來,他們將繼續支援類元件。在 Facebook 有數萬個類元件,他們表示絕對沒有計劃重寫它們。相反,他們開始在新程式碼中使用 Hooks。
怎樣使用 Hooks?
假設我們正在嘗試編寫一個 Counter 元件,正常的實現如下:
而基於 Hooks 的實現將如下:
使用 Hooks 的好處非常明顯:
-
減少程式碼實現;
-
共享邏輯,可以將邏輯解耦。
參考連結
https://reactjs.org/docs/hooks-intro.html#motivation
https://reactjs.org/docs/hooks-overview.html
活動推薦
今年12 月 7-8 日在北京國際會議中心舉辦的 ArchSummit 全球架構師技術峰會邀請了超過百位的國內外專業講師,並設定了前端技術專題,分享他們的最新黑科技和研發經驗。
目前大會 8 折優惠購票火熱進行中, 掃描以下圖片二維碼 或點選“ 閱讀原文 ”瞭解更多詳情!
如有疑問歡迎諮詢票務經理灰灰:17326843116(微信同號)