[譯] 理解 React Hooks

本週,Sophie Alpert 和我在 React Conf 上提出了 「Hooks」 提案,緊接着是 Ryan Florence 對 Hooks 的深刻介紹:html

我強烈推薦你們觀看這個開場演講,在這個演講裏,你們能夠了解到咱們嘗試使用 Hooks 提案去解決的問題。不過,花費一個小時看視頻也是時間上的巨大投入,因此我決定在下面分享一些關於 Hooks 的想法。前端

注意:Hooks 是 React 的實驗性提案。你無需如今就去學習它們。另請注意,這篇文章包含個人我的意見,並不必定表明 React 團隊的立場react

爲何須要 Hooks?

咱們知道組件和自上而下的數據流能夠幫助咱們將龐大的 UI 組織成小型、獨立、可複用的塊。可是,咱們常常沒法進一步拆分複雜的組件,由於邏輯是有狀態的,並且沒法抽象爲函數或其餘組件。這也就是人們有時說 React 不容許他們「分離關注點」的意思。android

這些狀況很常見,包括動畫、表單處理、鏈接到外部數據源以及其餘不少咱們但願在組件中作的事情。當咱們嘗試單獨使用組件來解決這些問題時,一般咱們會這樣收場:ios

  • 巨大的組件 難以重構和測試。
  • 重複的邏輯 在不一樣的組件和生命週期函數之間。
  • 複雜的模式 像 render props 和高階組件。

咱們認爲 Hooks 是解決全部這些問題的最佳實踐。Hooks 讓咱們將組件內部的邏輯組織成可複用的隔離單元git

Hooks 在組件內部應用 React 的哲學(顯式數據流和組合),而不只僅是組件之間。這就是爲何我以爲 Hooks 天生就適用於 React 的組件模型。github

不一樣於 render props 或高階組件等的模式,Hooks 不會在組件樹中引入沒必要要的嵌套。它們也沒有受到 mixins 的負面影響spring

即便你心裏一開始是抵觸的(就像我剛開始同樣!),我仍是強烈建議你直接對這個提案進行一次嘗試和探索。我想你會喜歡它的。編程

Hooks 會使 React 變得臃腫嗎?

在咱們詳細介紹 Hooks 以前,你可能會擔憂咱們經過 Hooks 只是向 React 添加了更多概念。這是一個公正的批評。我認爲雖然學習它們確定會有短時間的認知成本,不過最終的結果卻偏偏相反。後端

若是 React 社區接受 Hooks 的提案,這將減小編寫 React 應用時須要考慮的概念數量。Hooks 可使得你始終使用函數,而沒必要在函數、類、高階組件和 reader 屬性之間不斷切換。

就部署大小而言,對 Hooks 的支持僅僅增長了 React 約 1.5kB(min + gzip)的大小。雖然很少,但因爲使用 Hooks 的代碼一般能夠比使用類的等效代碼壓縮得更小,因此使用 Hooks 也可能會減小你的包大小。下面這個例子有點極端,但它有效地展現了我這麼說的緣由(點擊查看整個帖子):

Hooks 提案不包括任何重大變化。即便你在新編寫的組件中採用了 Hooks,你現有的代碼仍將照常運行。事實上,這正是咱們推薦的 —— 不作大的重寫!在任何關鍵代碼中採用 Hooks 都是一個好主意。與此同時,若是你可以嘗試 16.7 alpha 版並在 Hooks proposalreport any bugs 向在咱們提供反饋,咱們將不勝感激。

究竟什麼是 Hooks?

要了解 Hooks,咱們須要退一步來思考代碼複用。

今天,有不少方式能夠在 React 應用中複用邏輯。咱們能夠編寫一個簡單的函數並調用它們來進行某些計算。咱們也能夠編寫組件(它們自己能夠是函數或類)。組件更強大,但它們必須渲染一些 UI。這使得它們不便於共享非可視邏輯。這使得咱們最終不得不用到 render props 和高階組件等複雜模式。若是隻用一種簡單的方式來複用代碼而不是那麼多,那麼React會不會簡單點

函數彷佛是代碼複用的一種完美機制。在函數之間組織邏輯僅須要最少的精力。可是,函數內沒法包含 React 的本地狀態。在不重構代碼或不抽象出 Observables 的狀況下,你也沒法從類組件中抽象出「監視窗口大小並更新狀態」或「隨時間變化改變更畫值」的行爲。這兩種方法都破壞了咱們喜歡的 React 的簡單性。

Hooks 正好解決了這個問題。 Hooks 容許你經過調用單個函數以在函數中使用 React 的特性(如狀態)。React 提供了一些內置的 Hooks,它們暴露了 React 的「構建塊」:狀態、生命週期和上下文。

因爲 Hooks 是普通的 JavaScript 函數,所以你能夠將 React 提供的內置 Hooks 組合到你本身的「自定義 Hooks」中。這使你能夠將複雜問題轉換爲一行代碼,並在整個應用或 React 社區中分享它們:

注意,自定義 Hooks 從技術上講並非 React 的特性。編寫自定義 Hooks 的可行性源自於 Hooks 的設計方式。

來點代碼!

假設咱們想要將訂閱一個自適應當前窗口寬度的組件(例如,在有限的視圖上顯示不一樣的內容)。

如今你有幾種方法能夠編寫這種代碼。這些方法包括編寫類,設置一些生命週期函數,若是要在組件之間複用,甚至能夠須要提取 render props 或更高一層的組件。但我認爲沒有比這更好的了:

若是你看這段代碼,它偏偏就是我所表達的。咱們在咱們的組件中使用窗口的寬度,而 React 將會在它變化是從新渲染。這就是 Hooks 的目的 —— 使組件作到真正的聲明式,即便它們包含狀態和反作用。

讓咱們來看看如何實現這個自定義 Hooks。咱們使用 React 的本地狀態來保存當前窗口寬度,並在窗口調整大小時使用一個反作用來設置該狀態:

就像你從上面看到的那樣,像 useStateuseEffect 這樣做爲基本構建塊的 React 內置 Hooks。咱們能夠直接在組件中使用它們,或者咱們能夠將它們整合到自定義 Hooks 中,就像 useWindowWidth 那樣。使用自定義 Hooks 感受就像使用 React 的內置 API 同樣駕輕就熟。

你能夠從此概述中瞭解有關內置 Hooks 的更多信息。

Hooks 是徹底封裝的 —— 你每次調用 Hooks 函數, 它都會從當前執行組件中獲取到獨立的本地狀態。對這個特殊的例子來講並不重要(全部組件的窗口寬度是相同的!),但這正是 Hooks 如此強大的緣由。它們不只是一種共享狀態的方式,更是共享狀態化邏輯的方式。咱們不想破壞自上而下的數據流!

每一個 Hooks 均可以包含一些本地狀態和反作用。你能夠在不一樣 Hooks 之間傳值,就像在一般在函數之間作的那樣。Hooks 能夠接受參數並返回值,由於它們就是JavaScript 函數。

這是一個實驗 Hooks 的 React 動畫庫的例子:

在 CodeSandbox 上運行這個例子

注意,在演示代碼中,這個驚人的動畫是經過幾個自定義 Hooks 的傳值實現的。

(若是你想了解更多關於這個例子的信息, 查看此介紹。)

在 Hooks 之間傳遞數據的能力使得它們很是適合實現動畫、數據訂閱、表單管理和其餘狀態化的抽象。不一樣於 render props 和高階組件,Hooks 不會在渲染樹中建立「錯誤層次結構」。它們更像是一個鏈接到組件的「存儲單元」的平面列表。沒有額外的層。

類又該何去何從?

在咱們看來,自定義 Hooks 是 Hooks 提案中最吸引人的部分。可是爲了使自定義 Hooks 工做,React 須要爲函數提供一種聲明狀態和反作用的辦法。而這也正是像 useStateuseEffect 這樣的內置 Hooks 容許咱們作的事情。你能夠在文檔中瞭解它們。

事實證實,這些內置 Hooks 不只可用於建立自定義 Hooks。它們足以用來定義組件,由於它們像 state 同樣爲咱們提供了全部必要的特性。這就是爲何咱們但願 Hooks 成爲將來定義 React 組件的主要緣由。

咱們沒有打算棄用類。在 Facebook,咱們有成千上萬的類組件,並且和你同樣,咱們無心重寫它們。可是若是 React 社區接受了 Hooks,那麼同時推薦兩種不一樣的方式來編寫組件是沒有意義的。Hooks 能夠涵蓋類的全部應用場景,同時在抽象,測試和複用代碼方面提供更大的靈活性。這就是爲何 Hooks 表明了咱們對 React 將來的願景。

不過 Hooks 是否是有點「魔術化」?

你可能會對Hooks 的規則感到驚訝。

雖然必須在頂層調用 Hooks 是不尋常的,但即便能夠,你可能也不但願在某種條件判斷中定義狀態。例如,你也沒法對類中定義的狀態進行條件判斷,而在過去四年和 React 用戶的交流中,我也沒有聽到過對此的抱怨。

這種設計在不引入額外的語法噪音或其餘坑的狀況下,對自定義 Hooks 相當重要。咱們知道用戶一開始可能不熟悉,但咱們認爲這種取捨對將來是值得的。若是你不一樣意,我鼓勵你動手去實踐一下,看看這是否會改變你的感覺。

咱們已經在生產環境下使用 Hooks 一個月了,以觀察工程師們是否對這些規則感到困惑。咱們發現實際狀況是人們會在幾個小時內習慣它們。就我的而言,我認可這些規則起初讓我「感受不對」,但我很快就克服了它。此次經歷像極了我對 React 的第一印象。(你一開始就喜歡 React 嗎?至少我不是一開始就喜歡,直到更屢次嘗試後才改變見解。)

記住,在 Hooks 的實現中也沒有什麼「魔術」。就像 Jamie 指出的那樣,它像極了這個:

咱們爲每一個組件保留了一個 Hooks 列表,並在每次 Hooks 被調用時移動到列表中的下一項。得意於 Hooks 的規則,它們的順序在每次渲染中都是相同的,所以咱們能夠爲每次調用提供正確的組件狀態。要知道 React 不須要作任何特殊處理就能知道哪一個組件正在渲染 —— 調用你的組件的正是 React。

也許你在想 React 在哪裏保存了 Hooks 的狀態。答案就是,它保存在和 React 爲類保持狀態相同位置。不管你如何定義組件,React 都有一個內部的更新隊列,它是任何狀態的真實來源。

Hooks 不依賴於現代 JavaScript 庫中常見的代理或 getter。按理說,Hooks 比一些解決相似問題的流行方法日常。我想說 Hooks 就像調用 array.push 和 array.pop 同樣普通(同樣的取決於調用順序!)。

Hooks 的設計與 React 無關。事實上,在提案發布後的前幾天,不一樣的人提出了針對 Vue,Web Components 甚至原生 JavaScript 函數的相同 Hooks API 的實驗性實現。

最後,若是你是一個純函數編程主義者而且對 React 依賴可突變狀態的實現細節感到不安,你會欣喜的發現完成 Hooks 能夠以函數式編程的方式實現(若是 JavaScript 支持它們)。固然,React 一直依賴於內部的可突變狀態 —— 正因如此沒必要那樣作。

不管你是從一個更務實仍是教條的角度來考慮(或者你二者兼有),我但願至少有一個立場是有意義的。最重要的是,我認爲 Hooks 讓咱們用更少的精力去構建組件,並提供更好的用戶體驗。這就正是我我的對 Hooks 感到興奮的地方。

傳播正能量,而不是炒做

若是 Hooks 對你尚未什麼吸引力,我徹底能夠理解。我仍然但願你能在一個很小的項目上嘗試一下,看看是否會改變你的見解。不管你是遇到須要 Hooks 來解決的問題,仍是說你有不一樣的解決方案,歡迎經過 RFC 告訴咱們!

若是我你感到興奮,或者說有那麼點好奇,那就太好了!我只有一個問題要問。如今有不少人正在學習 React,若是咱們匆匆忙忙的編寫教程,並把僅僅纔出現幾天的功能宣稱爲最佳實踐,他們會感到困惑。即便對咱們在 React 團隊的人來講,關於 Hooks 的一些事情還不是很清楚。

若是你在 Hooks 不穩按期間開發了任何有關 Hooks 的內容,請突出提示它們是一個實驗性提案,幷包含指向官方文檔的連接。咱們會在提案發生任何改變時及時更新它。咱們也花了至關多的精力來完善它,因此不少問題已在那裏獲得瞭解決。

當你和其餘不像你那麼興奮的人交流時,請保持禮貌。若是你發現別人對此有誤解,若是對方樂意的話你能夠和他分享更多信息。但任何改變都是可怕的,做爲一個社區,咱們應該盡力幫助人們,而不是疏遠他們。若是我(或 React 團隊中的任何其餘人)未遵循此建議,請致電咱們!

更進一步

查看 Hooks 提案的文檔以瞭解更多信息:

Hooks 仍然處於早期階段,但咱們很樂意能聽到大家的反饋。你能夠直接去 RFC,與此同時,咱們也會盡可能及時回覆 Twitter 上的對話。

若是有不清楚的地方,請告訴我,我很樂意爲你答疑解惑。感謝你的閱讀!

Vitra — Portemanteau Hang it all

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索