前端進階(十一)React16.7-----Hooks

使用Hooks可以讓你在不適用類的情況下使用狀態和其他的一些react功能。可在此討論

Hooks概覽看這裏:https://reactjs.org/docs/hooks-overview.html

看下面代碼:

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.

無重大改變

  • 完全選擇加入: 你可以在幾個組件中嘗試Hooks,而無需重寫任何現有代碼。但是如果你不想這樣,你現在可以不用學習或使用Hooks
  • 100%向後兼容: Hooks不包含任何重大更改。
  • 現在可使用: Hook目前處於alpha版本,更希望在收到社區反饋後將它們包含在React 16.7

沒有計劃從React中刪除class

Hook不會取代你對React概念的瞭解。 相反,Hooks爲你已經知道的React概念提供了更直接的APIpropsstatecontextrefslifecycle。正如我們稍後將展示的,Hooks還提供了一種新的強大方式來組合它們。

動機

Hook解決了React中各種看似無關的問題,我們在編寫和維護數以萬計的組件時遇到了這些問題。無論你是在學習React,每天使用它,還是更喜歡使用具有類似組件模型的不同庫,你都可能會發現其中的一些問題。

在組件之間重用有狀態邏輯很困難

React沒有提供將可重用行爲“附加”到組件的方法(例如,將其連接到store)。如果你已經使用React一段時間,你可能熟悉渲染道具高階組件等模式,試圖解決這個問題。但是這些模式要求你在使用它們時重構組件,這可能很麻煩並且使代碼更難以遵循。如果你看一下React DevTools中一個典型的React應用程序,你可能會發現一個由包含提供者,消費者,高階組件,渲染道具和其他抽象層的組件組成的“包裝器地獄”。雖然我們可以在DevTools中過濾它們,但這指出了一個更深層次的基本問題:React需要一個更好的原語來共享有狀態邏輯。

使用Hook,你可以從組件中提取有狀態邏輯,以便可以獨立測試並重用。Hook允許您在不更改組件層次結構的情況下重用有狀態邏輯。 這樣可以輕鬆地在許多組件之間或與社區共享Hook

我們將在編寫自定義鉤子中進行更多討論。

複雜的組件變得難以理解

我們經常不得不維護一些開始簡單的組件,但最後卻變成了無法管理的狀態邏輯和副作用的情況。每個生命週期方法通常包含不相關邏輯的混合。例如,組件可能會在componentDidMountcomponentDidUpdate中執行一些數據提取。並且,相同的componentDidMount方法可能還包含一些設置事件偵聽器的無關邏輯,並在componentWillUnmount中執行清理。這樣,相互關聯的代碼被拆分,但完全不相關的代碼最終組合在一個方法中。這很容易引入錯誤和不一致。

在許多情況下,不可能將這些組件分解爲較小的組件,因爲狀態邏輯遍佈整個地方。測試它們也很困難。這是許多人更喜歡將React與單獨的狀態管理庫相結合的原因之一。但是,這通常會引入太多的抽象,要求在不同的文件之間跳轉,並使重用組件變得更加困難。

爲了解決這個問題,Hooks允許根據相關的部分(例如設置訂閱或獲取數據)將一個組件拆分爲較小的函數, 而不是基於生命週期方法強制拆分。你還可以選擇使用reducer管理組件的本地狀態,以使其更具可預測性。

更多地關於Effect Hook討論

class混淆了人和機器

在我們的觀察中,class是學習React的最大障礙。你必須瞭解thisJavaScript中是如何工作的,這與它在大多數語言中的工作方式有很大不同。你必須記住bind事件處理程序。沒有不穩定的語法提議,代碼非常冗長。開發者可以很好地理解propsstate和自上而下的數據流,但仍然很難與類鬥爭。React中的函數和類組件之間的區別以及何時使用每個組件導致即使在經驗豐富的React開發人員之間也存在分歧。

此外,React已經推出了大約五年,我們希望確保它在未來五年內保持相關性。正如SvelteAngularGlimmer和其他人所表明的那樣,提前編譯組件在未來有很大潛力。特別是如果它不限於模板。最近,我們一直在嘗試使用Prepack進行組件摺疊,我們已經看到了有希望的早期結果。但是,我們發現類組件可能會增長無意識的模式,使這些優化迴歸到較慢的路徑。類也爲今天的工具提出了問題。例如,類不會很好地縮小,並且它們使得熱加載片狀和不可靠。我們希望提供一種API,使代碼更有可能保持可優化途徑。

爲了解決這些問題,Hooks允許你在沒有類的情況下使用更多React的功能。 從概念上講,React組件一直更接近功能(function)。 Hooks擁抱功能,但不會犧牲React的實踐精神。鉤子提供了對命令式逃生艙口的訪問,並且不需要你學習複雜的功能或反應式編程技術。

Hooks概覽是開始學習Hooks的好地方。

逐步採用策略

沒有計劃從React中刪除類。

我們知道React開發人員專注於發佈產品,沒有時間研究正在發佈的每個新API。鉤子是非常新的,在考慮學習或採用它們之前等待更多示例和教程可能會更好。

我們也理解爲React添加新東西的標準非常高。對於好奇的讀者,我們已經準備了一個詳細的RFC,其中包含更多細節的動機,並提供有關特定設計決策和相關現有技術的額外視角。

至關重要的是,Hooks與現有代碼並行工作,因此您可以逐步採用它們。 我們正在分享這個實驗性的API,以便從社區中那些有興趣塑造React未來的人那裏獲得早期反饋 - 我們將在公開場合迭代Hooks

最後,沒有急於遷移到Hooks。我們建議避免任何“重大改寫”,特別是對於現有的複雜類組件。根據我們的經驗,最好先在新的和非關鍵組件中練習使用Hooks,並確保團隊中的每個人都對它們感到滿意。在嘗試Hooks之後,請隨時向我們發送反饋,無論是好的還是不好的。

我們打算讓Hooks涵蓋所有現有的類用例,但 我們將在可預見的未來繼續支持類組件。 在Facebook,我們有數萬個組件作爲類編寫,我們絕對沒有計劃重寫它們。相反,我們開始在新代碼中使用Hooks與類並排。

後續將繼續介紹對應的api,如需瞭解,可watch倉庫

 

之前我們介紹了使用hooks的原因,在開始介紹api之前,現在我們先來整體的預覽下這些api。 從上篇的介紹可以知道,Hook是向後兼容的,有react開發經驗的你看起來會更順暢。

是一個快節奏的概述。如果你感到困惑,可以看下上面提到的介紹裏的動機:

詳細說明 閱讀動機以瞭解我們爲何將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(我們將在稍後討論這意味着什麼)。可以看到,在這個函數組件裏,我們向他添加一些本地狀態。React將在重新渲染之間保留這狀態。 useState返回一對:當前狀態值(count)和允許你更新狀態的函數(setCount)。你可以從事件處理程序或其他位置調用此函數。這個函數類似於類中的this.setState,但是它不會將舊狀態和新狀態合併在一起。(我們將在使用State Hook中顯示一個將useStatethis.state進行比較的示例。)

useState的唯一參數是初始狀態。 在上面的例子中,它是0,因爲我們的計數器從零開始。請注意,與this.state不同,此處的狀態不必是對象 - 儘管可以是任何你想要的。初始狀態參數僅在第一次渲染期間使用。

聲明多個state

你可以在一個組件中多次使用State Hook

function ExampleWithManyStates() {
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
  // ...
}

數組解構語法允許我們爲通過調用useState聲明的狀態變量賦予不同的名稱。這些名稱不是useState API的一部分。相反,React假定如果多次調用useState,則在每次渲染期間以相同的順序執行。我們將回到爲什麼這種方法有效以及何時有用。

什麼是Hook

鉤子是允許從功能組件(function component)“掛鉤”React狀態和生命週期功能的功能。鉤子在類內部不起作用 - 它們允許你在沒有類的情況下使用React (我們不建議你在一夜之間重寫現有組件,但如果你願意,可以開始在新組件中使用Hook。)

React提供了一些像useState這樣的內置Hook。你還可以創建自定義Hook以在不同組件之間重用有狀態行爲。我們先來看看內置的Hooks

詳細說明 你可以在使用State Hook中瞭解更多信息。

Effect Hook

你之前可能已經從React組件執行數據提取,訂閱或手動更改DOM。我們將這些操作稱爲“副作用”(或簡稱爲“效果”),因爲它們會影響其他組件,並且在渲染過程中無法完成。

Effect Hook中的useEffect增加了在功能組件執行副作用的功能。它與React類中的componentDidMountcomponentDidUpdatecomponentWillUnmount具有相同的用途,但統一爲單個API。(我們將在使用Effect Hook時顯示將useEffect與這些方法進行比較的示例。)

例如,下面的組件將在React更新DOM後設置文檔標題:

import { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // 類似componentDidMount 和 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的更改後(你可以認爲是render之後)。 效果在組件內聲明,因此可以訪問其propsstate。默認情況下,React在每次渲染後運行效果 - 包括第一次渲染。 

Effects還可以通過指定返回函數來清理他們。看下面的這個例子:

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';
}

在這個示例中,當組件卸載時,以及在由於後續渲染而重新運行效果之前,React將取消訂閱我們的ChatAPI。(如果你願意的話,如果我們傳遞給ChatAPIprops.friend.id沒有改變,有辦法告訴React跳過重新訂閱。)

就像使用useState一樣, 你也可以在組件中使用多個useEffect:

function FriendStatusWithCounter(props) {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

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

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

詳細說明 你可以在使用Effect Hook中瞭解更多信息。

Hooks的規則

鉤子是JavaScript函數,但它們強加了兩個額外的規則:

  • 只能在頂層調用Hooks。不要在循環,條件或嵌套函數中調用Hook
  • 僅從React功能組件調用Hooks 不要從常規JavaScript函數中調用Hook。 (還有另一個有效的地方叫Hooks - 你自己的定製Hooks。我們馬上就會了解它們。)

詳細說明 你可以在Rules Hook中瞭解更多信息。

Custom Hooks

有時,我們希望在組件之間重用一些有狀態邏輯的部分傳統上,這個問題有兩個流行的解決方案:高階組件渲染道具Custom Hooks允許你執行這樣的操作,並且無需向樹中添加更多組件。在上面我們介紹了一個調用useStateuseEffect HooksFriendStatus組件來訂閱朋友的在線狀態。假設我們還希望在另一個組件中重用此訂閱邏輯。 首先,我們將這個邏輯提取到一個名爲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>
  );
}

這些組件的狀態是完全獨立的。鉤子是重用有狀態邏輯的一種方式,而不是狀態本身。 事實上,每次調用Hook都有一個完全隔離的狀態 - 所以你甚至可以在一個組件中使用相同的自定義Hook兩次。

custom hook更像是一種約定而非功能。如果函數的名稱以use開頭並且它調用其他Hook,我們說它是一個Custom HookuseSomething命名約定是linter插件如何使用Hooks在代碼中查找錯誤的。

詳細說明 你可以在Writing Custom Hooks中瞭解更多信息。

Other Hooks

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

function Example() {
  const locale = useContext(LocaleContext);
  const theme = useContext(ThemeContext);
  // ...
}

useReducer允許使用reducer管理複雜組件的本地狀態:

function Todos() {
  const [todos, dispatch] = useReducer(todosReducer);
  // ...
}

詳細說明 你可以在 Hooks API Reference.中瞭解更多信息。或者這裏https://www.jianshu.com/p/6c43b440dab8

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章