學習如何使用 Hooks 建立 React 應用程式

Hooks 是在 React 16.7 中引入的功能,它將改變我們未來編寫 React 應用程式的方式。

在出現 Hooks 之前,某些關鍵的功能只能在類別組件中實現:擁有自己的狀態和使用生命週期事件。輕巧靈活的函式組件在功能上受到了限制。

Hooks 允許函式組件擁有狀態並響應生命週期事件,並且幾乎使類別組件過時。它們還允許函式組件以良好的方式處理事件。

存取狀態

使用 useState() API,您可以創建一個新的狀態變量並對其進行更改。useState() 接受狀態項目的初始值並返回包含狀態變量和修改狀態的函數的陣列。由於它返回的是一個陣列,我們使用陣列解構來訪問每個單獨的項目,就像這樣:const [count, setCount] = useState(0)

以下是一個實際示例:

import { useState } from 'react'

const Counter = () => {
  const [count, setCount] = useState(0)

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  )
}

ReactDOM.render(<Counter />, document.getElementById('app'))

您可以添加任意多個 useState() 調用,以創建任意多個狀態變量。只需確保在組件的頂層調用它(不在 if 或其他塊中)。

Codepen 上的示例:

React Hooks example #1 counter

存取生命週期鉤子

Hooks 的另一個非常重要的特性是允許函式組件存取生命週期鉤子。

使用類別組件,您可以在 componentDidMountcomponentWillUnmountcomponentDidUpdate 事件上註冊函數,並且這些鉤子可用於從變數初始化到 API 調用和清理等多種用例。

Hooks 提供了 useEffect() API。這個調用接受一個函數作為參數。

這個函數在組件首次渲染時運行,在每次後續重新渲染/更新時都會運行。React 首先更新 DOM,然後調用傳遞給 useEffect() 的任何函數。這樣即使在阻塞代碼上,也不會阻塞 UI 渲染,不像舊的 componentDidMountcomponentDidUpdate 那樣,這使我們的應用程式感覺更快。

示例:

const { useEffect, useState } = React

const CounterWithNameAndSideEffect = () => {
  const [count, setCount] = useState(0)
  const [name, setName] = useState('Flavio')

  useEffect(() => {
    console.log(`Hi ${name} you clicked ${count} times`)
  })

  return (
    <div>
      <p>
        Hi {name} you clicked {count} times
      </p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
      <button onClick={() => setName(name === 'Flavio' ? 'Roger' : 'Flavio')}>
        Change name
      </button>
    </div>
  )
}

ReactDOM.render(
  <CounterWithNameAndSideEffect />,
  document.getElementById('app')
)

通過從我們的 useEffect() 參數中返回一個函數,也可以實現相同的 componentWillUnmount 的功能:

useEffect(() => {
  console.log(`Hi ${name} you clicked ${count} times`)
  return () => {
    console.log(`Unmounted`)
  }
})

useEffect() 可以被多次調用,這對於分離不關聯的邏輯(這是類組件的生命週期事件的困擾之一)非常有用。

由於 useEffect() 函數在每次後續重新渲染/更新時運行,我們可以通過添加作為第二個參數的陣列來告訴 React 略過運行,以提高性能。這個陣列包含要觀察的狀態變量的列表。只有在這個陣列中的項目之一發生變化時,React 才會重新運行這個副作用。

useEffect(() => {
  console.log(`Hi ${name} you clicked ${count} times`)
}, [name, count])

同樣地,您可以通過傳遞一個空陣列來告訴 React 只執行一次副作用(在掛載時):

useEffect(() => {
  console.log(`Component mounted`)
}, [])

useEffect() 非常適合添加日誌、訪問第三方 API 等等。

Codepen 上的示例:

React Hooks example #3 side effects

使用自定義鉤子實現組件間通信

撰寫自定義鉤子的能力是未來將顯著改變您撰寫 React 應用程式的特性。

使用自定義鉤子,您可以在組件之間共享狀態和邏輯,類似於 render props 和高階組件的模式,它們仍然非常好,但是現在自定義鉤子在許多用例中已經不那麼重要。

如何創建自定義鉤子?

鉤子是一個以 use 開頭的函數,通常支持任意數量的參數,並且可以返回任何你想要的東西。

示例:

const useGetData() {
  //...
  return data
}

或者

const useGetUser(username) {
  //...const user = fetch(...)
  //...const userData = ...
  return [user, userData]
}

在您自己的組件中,可以像這樣使用鉤子:

const MyComponent = () => {
  const data = useGetData()
  const [user, userData] = useGetUser('flavio')
  //...
}

確定何時使用鉤子而不是常規函數應該根據具體情況來決定,只有經驗才能告訴你。