探索 React Hooks:原來它們是這樣誕生的!

這篇文章《Where Did Hooks Come From?》主要討論了 React Hooks 的來源和背景。在引入 Hooks 之前,React 類需要擴展 React.Component 或 React.PureComponent,而 React 本身沒有提供共享代碼的 API。因此,React 社區開發人員創建了兩種有效共享組件代碼的模式,分別是高階組件(Higher Order Components,簡稱 HOC)和 Render Props。這些模式在一定程度上解決了代碼重用的問題,但仍然存在一些侷限性。爲了更好地解決這些問題,React Hooks 被引入,爲開發者提供了一種更簡潔、易於理解的方式來共享和重用組件的邏輯。

下面是正文~~

Hooks 是用於在組件之間共享通用邏輯的。明確地說,我們所說的 “邏輯” 並不是指組件的 UI 部分(JSX)。我們談論的是組件中 JSX 之前的所有內容。在基於類的組件中,我們會說它在生命週期方法和自定義方法中。在功能組件中,它只是 JSX 之上的東西。

在某種程度上,Hooks 的故事與 React 及其先前用於共享代碼的 API 的故事密切相關。所以請耐心聽我從頭說起...

2013:第一個 React API:

React 開發者不喜歡 mixins,這是共享邏輯的第一個 API。

最初,React 有一種在組件之間共享通用邏輯的方法,稱爲 mixins。這是在 JavaScript 擁有類之前的 React 早期。這些僞類看起來的組件允許 “混入” 可共享的邏輯。當時,mixins 被指責爲社區開始流行的一些反模式的根本原因。因此,當 React 在 2016 年獲得真正的類時,大多數 React 開發人員爲 mixins 的 API 消失而歡呼。

2016:類組件

在 JavaScript 在 ES2015(ES6)中獲得類之後,React 很快跟進了今天仍然可以使用的類組件。但是,如果你對 React 較爲陌生,可能會想知道爲什麼普遍認爲應該在 React 中完全避免使用類組件?

主要原因是共享邏輯困難。當我們失去了 mixins 時,我們也失去了一種原始的共享代碼方式。我們可以通過創建一個新組件來共享 / 重用 UI,以共享 JSX,但是沒有內置方法可以共享生命週期方法,例如 componentDidMountcomponentDidUpdatecomponentWillUnmount 。 這些特定方法是我們可能希望管理組件副作用的地方。因此,如果您用某個副作用編寫 ComponentOne ,我們將不得不將該邏輯複製到 ComponentTwo ,從而使邏輯無法以一種只編寫一次的方式抽象。

我們不能用繼承嗎?

class ComponentOne extends SharableStuff {
  // ...
}

class ComponentTwo extends SharableStuff {
  // ...
}

不,React 不允許我們編寫從其他組件繼承的組件。而且,即使 React 允許你這樣做,你將如何將多個邏輯體共享到 ComponentOne ?不允許多重繼承,所以這不起作用:

class ComponentOne extends SharableStuffA extends SharableStuffB {
  // ...
}

React 類必須擴展 React.ComponentReact.PureComponent ,並且 React 本身沒有共享代碼的 API。

社區雖然很聰明。React 開發人員創建了兩種模式,有效地在組件之間共享代碼,這兩種模式被稱爲高階組件(Hoc)和 Render Props

無狀態函數組件

在同一時期,React 團隊宣佈了一種使用函數而不是類來創建組件的新方法。當時的主要想法是擁有一個僅接受屬性並可以返回 JSX 的組件。沒有狀態或使用類似於類生命週期方法的 React API 的能力。

我們稱之爲無狀態函數組件,因爲它們也不能有狀態。

不久之後,React 團隊告訴我們不要這樣稱呼它們。我們應該稱之爲函數組件,因爲... 他們有計劃🤔

2018 Hooks

從本質上講,Hooks 只是我們可以從函數組件中調用的函數。我們可以使用內置的鉤子並編寫自己的:

React 有 useState() ,因此函數組件可以擁有與類狀態類似的自己的本地狀態。但是,如果刷新頁面,所有本地狀態都會重置(就像任何其他 JS 變量一樣)。因此,我們可以創建自己的 useLocalStorageState() ,它可能的工作方式與 useState() 完全相同,但還將狀態保持到 localStorage ,以便在刷新後恢復值。

下面是一個使用自定義鉤子共享數據獲取邏輯的示例。你不必完全瞭解如何使用 useStateuseEffect ,只需要瞭解它們爲組件執行一些邏輯,我想共享它。如果另一個組件也想根據 productId 獲取產品,那麼需要重新編寫下面高亮的代碼:

這裏是相同的邏輯移至自定義鉤子。現在任何組件都可以使用 useFetchProduct 鉤子:

// Custom Hook
function useFetchProduct(productId) {
  const [product, setProduct] = useState(null)

  useEffect(() ={
    fetchProduct(productId).then((product) => setProduct(product))
  }[productId])

  return product
}

function BrowseProducts({ productId }) {
  const product = useFetchProduct(productId)
  // return <div>...</div>
}

這是一個過於簡化的例子,上面的 useEffect 代碼是不完整的。如果你想要一個獲取數據的自定義 Hook,推薦來自 React Query 的自定義鉤子,名爲 useQuery()

如今,如果你願意,你仍然可以使用類。如果你覺得它們更容易使用,那完全取決於你。然而,在類之間共享邏輯時,你將會遇到問題。即使你可以接受這些問題,並且你不覺得高階組件(HOC)和 Render Props 混亂,與過去五年開始學習 React 的其他開發者合作或者組隊工作時,你可能會發現困難。

他們在 Hooks 被當作 React 主要方法教授時開始接觸 React。他們可能不瞭解類組件的 “進退維谷”,如何處理這種奇怪的作用域問題,以及何時以及如何使用 HOC 或 Render Props。此外,React 生態系統中絕大多數第三方庫已經放棄了 HOC 和 Render Props,轉而採用了 Hooks。因此,你將無法輕鬆地使用它們的工具,因爲 Hooks 僅適用於函數式組件。

我的一些朋友已經使用 React 很長時間了,他們記得這些討論和權衡。但是我注意到(至少在 Twitter 上),歷史正在重演。有一整代新的 React 開發者不知道這個背景故事,也不知道我們爲什麼要有 Hooks。我承認,Hooks 的某些部分比類更難,比如我們可能需要記憶化( useMemouseCallback ),但這是一種權衡。你可以選擇使用帶有 HoC 和 Render Props 的類(也不容易),或者使用具有輕鬆共享代碼能力的 Hooks,但需要理解記憶化的複雜性。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/h9ov_yREuglMXOvnCbTRpQ