- 原文地址:Making Sense of React Hooks
- 原文做者:Dan Abramov
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:HaoChuan9421
- 校對者:calpa, Ivocin
本週,Sophie Alpert 和我在 React Conf 上提出了 「Hooks」 提案,緊接着是 Ryan Florence 對 Hooks 的深刻介紹:html
我強烈推薦你們觀看這個開場演講,在這個演講裏,你們能夠了解到咱們嘗試使用 Hooks 提案去解決的問題。不過,花費一個小時看視頻也是時間上的巨大投入,因此我決定在下面分享一些關於 Hooks 的想法。前端
注意:Hooks 是 React 的實驗性提案。你無需如今就去學習它們。另請注意,這篇文章包含個人我的意見,並不必定表明 React 團隊的立場。react
咱們知道組件和自上而下的數據流能夠幫助咱們將龐大的 UI 組織成小型、獨立、可複用的塊。可是,咱們常常沒法進一步拆分複雜的組件,由於邏輯是有狀態的,並且沒法抽象爲函數或其餘組件。這也就是人們有時說 React 不容許他們「分離關注點」的意思。android
這些狀況很常見,包括動畫、表單處理、鏈接到外部數據源以及其餘不少咱們但願在組件中作的事情。當咱們嘗試單獨使用組件來解決這些問題時,一般咱們會這樣收場:ios
咱們認爲 Hooks 是解決全部這些問題的最佳實踐。Hooks 讓咱們將組件內部的邏輯組織成可複用的隔離單元:git
Hooks 在組件內部應用 React 的哲學(顯式數據流和組合),而不只僅是組件之間。這就是爲何我以爲 Hooks 天生就適用於 React 的組件模型。github
不一樣於 render props 或高階組件等的模式,Hooks 不會在組件樹中引入沒必要要的嵌套。它們也沒有受到 mixins 的負面影響。spring
即便你心裏一開始是抵觸的(就像我剛開始同樣!),我仍是強烈建議你直接對這個提案進行一次嘗試和探索。我想你會喜歡它的。編程
在咱們詳細介紹 Hooks 以前,你可能會擔憂咱們經過 Hooks 只是向 React 添加了更多概念。這是一個公正的批評。我認爲雖然學習它們確定會有短時間的認知成本,不過最終的結果卻偏偏相反。後端
若是 React 社區接受 Hooks 的提案,這將減小編寫 React 應用時須要考慮的概念數量。Hooks 可使得你始終使用函數,而沒必要在函數、類、高階組件和 reader 屬性之間不斷切換。
就部署大小而言,對 Hooks 的支持僅僅增長了 React 約 1.5kB(min + gzip)的大小。雖然很少,但因爲使用 Hooks 的代碼一般能夠比使用類的等效代碼壓縮得更小,因此使用 Hooks 也可能會減小你的包大小。下面這個例子有點極端,但它有效地展現了我這麼說的緣由(點擊查看整個帖子):
Hooks 提案不包括任何重大變化。即便你在新編寫的組件中採用了 Hooks,你現有的代碼仍將照常運行。事實上,這正是咱們推薦的 —— 不作大的重寫!在任何關鍵代碼中採用 Hooks 都是一個好主意。與此同時,若是你可以嘗試 16.7 alpha 版並在 Hooks proposal 和 report any bugs 向在咱們提供反饋,咱們將不勝感激。
要了解 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 的本地狀態來保存當前窗口寬度,並在窗口調整大小時使用一個反作用來設置該狀態:
就像你從上面看到的那樣,像 useState 和 useEffect 這樣做爲基本構建塊的 React 內置 Hooks。咱們能夠直接在組件中使用它們,或者咱們能夠將它們整合到自定義 Hooks 中,就像 useWindowWidth 那樣。使用自定義 Hooks 感受就像使用 React 的內置 API 同樣駕輕就熟。
你能夠從此概述中瞭解有關內置 Hooks 的更多信息。
Hooks 是徹底封裝的 —— 你每次調用 Hooks 函數, 它都會從當前執行組件中獲取到獨立的本地狀態。對這個特殊的例子來講並不重要(全部組件的窗口寬度是相同的!),但這正是 Hooks 如此強大的緣由。它們不只是一種共享狀態的方式,更是共享狀態化邏輯的方式。咱們不想破壞自上而下的數據流!
每一個 Hooks 均可以包含一些本地狀態和反作用。你能夠在不一樣 Hooks 之間傳值,就像在一般在函數之間作的那樣。Hooks 能夠接受參數並返回值,由於它們就是JavaScript 函數。
這是一個實驗 Hooks 的 React 動畫庫的例子:
注意,在演示代碼中,這個驚人的動畫是經過幾個自定義 Hooks 的傳值實現的。
(若是你想了解更多關於這個例子的信息, 查看此介紹。)
在 Hooks 之間傳遞數據的能力使得它們很是適合實現動畫、數據訂閱、表單管理和其餘狀態化的抽象。不一樣於 render props 和高階組件,Hooks 不會在渲染樹中建立「錯誤層次結構」。它們更像是一個鏈接到組件的「存儲單元」的平面列表。沒有額外的層。
在咱們看來,自定義 Hooks 是 Hooks 提案中最吸引人的部分。可是爲了使自定義 Hooks 工做,React 須要爲函數提供一種聲明狀態和反作用的辦法。而這也正是像 useState 和 useEffect 這樣的內置 Hooks 容許咱們作的事情。你能夠在文檔中瞭解它們。
事實證實,這些內置 Hooks 不只可用於建立自定義 Hooks。它們也足以用來定義組件,由於它們像 state 同樣爲咱們提供了全部必要的特性。這就是爲何咱們但願 Hooks 成爲將來定義 React 組件的主要緣由。
咱們沒有打算棄用類。在 Facebook,咱們有成千上萬的類組件,並且和你同樣,咱們無心重寫它們。可是若是 React 社區接受了 Hooks,那麼同時推薦兩種不一樣的方式來編寫組件是沒有意義的。Hooks 能夠涵蓋類的全部應用場景,同時在抽象,測試和複用代碼方面提供更大的靈活性。這就是爲何 Hooks 表明了咱們對 React 將來的願景。
你可能會對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 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。