Hooks概覽(譯)

shirleyyu發表於2018-12-09

原文:reactjs.org/docs/hooks-…

Hooks是React v16.7.0-alpha中加入的新特性。它可以讓你在class以外使用state和其他React特性。你可以在這裡看到關於它的一些討論。

Hooks 向後相容。本頁向有經驗的 React 使用者提供 Hooks 的概覽。

這是一個快節奏的概覽。如果你感到困惑,請在以下黃色方框中獲取更多相關內容:

詳細解釋

閱讀動機以瞭解我們為何將Hooks引入React

每個部分都以上面這樣的黃色框結束。它們連結到詳細的解釋。

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(稍後將會討論它的含義)。在函式元件中呼叫useState來向它新增一些本地state。React將在重新渲染之間保留此狀態。useState返回一對值:當前 state 值和一個用於更新這個值的函式。可以在事件處理程式或者其它地方呼叫這個函式。它類似於類中的this.setState。不同的是它不能將舊的state和新的state合併在一起。(我們將提供一個示例,用State Hook對useState和this.state進行比較。)

useState的唯一引數用於初始化state。在上面的例子中,這個初始值是0,因為計數器從0開始。請注意,與this.state不同的是,此處的state 不必是物件——儘管它支援物件型別。這個初始化state的引數僅在第一次渲染被使用。

宣告多個state變數

可以在單個元件中多次使用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時將宣告的state變數賦予不同的名稱。這些名稱不是useState API的一部分。相反,React假定如果多次呼叫useState,則在每次渲染時以相同的順序執行。 我們稍後將討論為什麼這種方法可行以及何時有用。

Hook是什麼?

Hooks是一個“鉤住”React state和生命週期特性的函式元件。Hooks在類中不起作用——它們讓你在沒有類的情況下使用React。(不建議一夜之間重寫現有元件,但如果你願意,可以開始在新元件中使用 Hooks。)

React提供了一些像useState這樣的內建Hook。你還可以建立自己的Hook以複用不同元件之間的狀態行為。我們先來看看內建的Hooks。

詳細解釋

你可以在專屬頁上了解有關State Hook的更多資訊:使用State Hook

Effect Hook

你之前可能從React元件執行過資料獲取、訂閱或手動更改DOM。我們將這些操作稱為“副作用”(或簡稱為“影響”)(side effects),因為它們會影響其他元件,並且在渲染過程中無法完成。

Effect Hook、useEffect增加了從功能元件執行副作用的功能。它與React類中的componentDidMount,componentDidUpdate和componentWillUnmount具有相同的用途,但統一為單個API。(我們將在使用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)函式。Effects函式在元件內被宣告,因此可以訪問其props和state。預設情況下,React在每次渲染後都執行effects函式——包括第一次渲染。 (我們將在使用Effect Hook章節中更多地討論這與類中的生命週期的比較。)

Effects還可以通過返回函式指定如何“清理”它們。例如,一個元件使用 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跳過重新訂閱。)

Hooks允許通過那些相關的部分(例如新增和刪除訂閱)來組織元件中的副作用(side effects),而不是基於生命週期方法強制拆分。

詳細解釋

你可以在專屬頁上了解有關useEffect的更多資訊:使用Effect Hook

Hooks 規範

Hooks是JavaScript函式,但它們強加了兩個額外的規則:

  • 只能在函式的頂層呼叫Hooks。不要在迴圈、條件或巢狀函式中呼叫Hook。
  • 只能在React的函式元件中呼叫Hooks,不能在常規JavaScript函式呼叫。(還有另一個呼叫Hooks的有效方式:自定義Hooks。稍後將會介紹它們。)

我們提供了一個linter外掛來自動執行這些規則。這些規則最初可能看起來是一種限制或令人困惑,但它們對於使Hooks執行良好必不可少。

詳細解釋

你可以在專屬頁上了解有關規範的更多資訊:Hook規範

自定義Hooks

有時,我們希望在元件之間複用一些狀態邏輯。這個問題在傳統方式上有兩種流行的解決方案:高階元件和render props。自定義Hooks可以解決這個問題,且無需向樹中新增更多元件。

在本頁前面,我們介紹了一個呼叫useState和useEffect Hooks的FriendStatus元件來訂閱朋友的線上狀態。我們希望在另一個元件中複用此訂閱邏輯。

首先,我們將這個邏輯提取到一個名為useFriendStatus的自定義Hook中:

import { useState, useEffect } from 'react';

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}
複製程式碼

它將friendID作為引數,並返回我們的朋友是否線上。

現在我們可以在兩個元件中使用它:

function FriendStatus(props) {
  const isOnline = useFriendStatus(props.friend.id);

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}
複製程式碼
function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}
複製程式碼

這些元件的狀態是完全獨立的。Hooks這種方式是複用狀態邏輯,而不是state本身。實際上,每次呼叫Hook都是一個完全隔離的狀態,所以你甚至可以在一個元件中兩次呼叫相同的自定義Hook。

自定義Hooks更像是一種約定而非功能。如果函式的名稱以“use”開頭並且它可以呼叫其他Hook,則稱之為自定義Hook。useSomething命名約定是為了讓linter外掛在使用Hooks的程式碼中查詢錯誤。

自定義Hook應用廣泛,如表單處理、動畫、宣告訂閱、計時器,以及可能還有更多我們沒有考慮到的。 我們很高興地期待React社群將提出什麼樣的自定義Hooks。

詳細解釋

你可以在專屬頁上了解有關自定義Hooks的更多資訊:自定義Hooks

其它Hooks

你可能會發現一些不太常用的內建Hook很有用。例如,useContext允許你訂閱React上下文而不用引入巢狀。

function Example() {
  const locale = useContext(LocaleContext);
  const theme = useContext(ThemeContext);
  // ...
}
複製程式碼

useReducer允許你使用reducer管理複雜元件的本地state:

function Todos() {
  const [todos, dispatch] = useReducer(todosReducer);
  // ...
複製程式碼
詳細解釋

你可以在專屬頁上了解更多有關內建Hooks的資訊:Hooks API參考

下一步

這一頁都是一些概括性的介紹。如果有些地方不瞭解或者想詳細瞭解更多內容,請閱讀下面的章節,從State Hook文件開始。

你還可以檢視Hooks API參考Hooks常見問題解答

最後,不要錯過介紹頁,它解釋了為什麼我們要新增Hooks以及我們如何開始將它們與類一起使用而無需重寫我們的應用程式。

相關文章