React 16.7.0-alpha hooks 之自定義鉤子

轉載於免費視頻網www.rails365.nethtml

構建本身的Hook能夠將組件邏輯提取到可重用的函數中。react

當咱們學習使用`Effect Hook時, 咱們從聊天應用程序中看到了這個組件,該組件顯示一條消息,指示朋友是在線仍是離線:git

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';
}
複製代碼

如今讓咱們的聊天應用程序也有一個聯繫人列表,咱們想要呈現綠色的在線用戶名。咱們能夠將上面相似的邏輯複製並粘貼到咱們的FriendListItem組件中, 但它不是很好:github

import { useState, useEffect } from 'react';

function FriendListItem(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);
    };
  });

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

相反,咱們想在FriendStatusFriendListItem之間分享這個邏輯。函數

傳統上,在React中,咱們有兩種流行的方式來共享組件之間的狀態邏輯:渲染道具和高階組件。咱們如今將看看Hook如何在不添加這些方法的狀況下解 決許多相同的問題。學習

提取自定義鉤子

當咱們想要在兩個JavaScript函數之間共享邏輯時,咱們將它提取到第三個函數。組件和掛鉤都是功能,因此這也適用於它們!測試

自定義Hook其實也是一個JavaScript函數,其名稱以use開頭,能夠調用其餘Hook 例如,下面的useFriendStatus是咱們的第一個自定義鉤子:動畫

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;
}
複製代碼

裏面沒有任何新內容 - 邏輯是從上面的組件中複製的。就像在組件中同樣,確保只在自定義Hook的頂層無條件地調用其餘Hookspa

React組件不一樣,自定義Hook不須要具備特定簽名。咱們能夠決定它做爲參數須要什麼,以及它應該返回什麼(若是有的話)。換句話說,它就像一個普通的功能。 它的名字應該始終使用use開頭,這樣你就能夠一眼就看出鉤子的規則適用於它。.net

咱們使用FriendStatus Hook的目的是訂閱咱們朋友的狀態。這就是爲何它將friendID做爲參數,並返回此朋友是否在線:

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

  return isOnline;
}
複製代碼

使用自定義鉤子

最初,咱們的目標是從FriendStatusFriendListItem組件中刪除重複的邏輯。他們倆都想知道朋友是否在線。

如今咱們已經將這個邏輯提取到useFriendStatus鉤子,咱們可使用它:

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設計的約定,而不是React功能。

我是否必須以use開頭命名個人自定義Hook 咱們但願你作到這點。這個習慣很重要。若是沒有它,咱們將沒法自動檢查是否 違反了Hook規則,由於咱們沒法判斷某個函數是否包含對 其中的Hooks的調用。

兩個組件使用相同的Hook共享狀態嗎? 不會。自定義掛鉤是一種重用有狀態邏輯的機制(例如設置訂閱和記住當前值),但每次使用自定義掛鉤時, 自定義掛鉤的全部狀態和效果都是徹底獨立的。

自定義Hook如何得到隔離狀態? 每次對Hook的調用都會被隔離。由於咱們直接調用useFriendStatus,從React的角度來看, 咱們的組件只調用useStateuseEffect。 正如咱們以前-所知,咱們能夠在一個組件中屢次調用useStateuseEffect,它們將徹底獨立。

提示:在掛鉤之間傳遞信息

因爲Hooks是函數,咱們能夠在它們之間傳遞信息。

爲了說明這一點,咱們將使用咱們假設的聊天示例中的另外一個組件。這是一個聊天消息收件人選擇器,顯示當前所選朋友是否在線:

const friendList = [
  { id: 1, name: 'Phoebe' },
  { id: 2, name: 'Rachel' },
  { id: 3, name: 'Ross' },
];

function ChatRecipientPicker() {
  const [recipientID, setRecipientID] = useState(1);
  const isRecipientOnline = useFriendStatus(recipientID);

  return (
    <>
      <Circle color={isRecipientOnline ? 'green' : 'red'} />
      <select
        value={recipientID}
        onChange={e : setRecipientID(Number(e.target.value))}
      >
        {friendList.map(friend : (
          <option key={friend.id} value={friend.id}>
            {friend.name}
          </option>
        ))}
      </select>
    </>
  );
}
複製代碼

咱們將當前選擇的friend ID保留在recipientID狀態變量中,若是用戶在<select>選擇器中選擇其餘朋友,則更新它。

由於useState Hook調用爲咱們提供了recipientID狀態變量的最新值,因此咱們能夠將它做爲參數傳遞給咱們的自定義useFriendStatus Hook

const [recipientID, setRecipientID] = useState(1);
const isRecipientOnline = useFriendStatus(recipientID);
複製代碼

這讓咱們知道當前選擇的朋友是否在線。若是咱們選擇不一樣的朋友並更新recipientID狀態變量,咱們的useFriendStatus Hook將取消訂閱以前選擇的朋友, 並訂閱新選擇的朋友的狀態。

使用你想象中的鉤子

Custom Hooks提供了之前在React組件中沒法實現的共享邏輯的靈活性。您能夠編寫自定義Hook,涵蓋普遍的用例,如表單處理,動畫,聲明訂閱,計時器, 以及可能還有更多咱們沒有考慮過的。更重要的是,能夠構建與React的內置功能同樣易於使用的Hook

儘可能抵制過早添加抽象。既然功能組件能夠作得更多,那麼代碼庫中的平均功能組件可能會變得更長。這是正常的 - 不要以爲你必須當即將它分紅鉤子。 但咱們也鼓勵你開始發現自定義Hook能夠隱藏簡單接口背後的複雜邏輯或幫助解開凌亂組件的狀況。

例如,可能有一個複雜的組件,其中包含許多以ad-hoc方式管理的本地狀態。 useState不會使更新邏輯更容易集中化,所以你可能但願將其 編寫爲Redux reducer

function todosReducer(state, action) {
  switch (action.type) {
    case 'add':
      return [...state, {
        text: action.text,
        completed: false
      }];
    // ... other actions ...
    default:
      return state;
  }
}
複製代碼

reducer很是便於單獨測試,而且能夠擴展以表達複雜的更新邏輯。若有必要,能夠將它們分紅更小的reducer。可是,你可能還享受使用React本地狀態的好處, 或者可能不想安裝其餘庫。

那麼,若是咱們能夠編寫一個useReducer Hook,讓咱們使用reducer管理組件的本地狀態呢?它的簡化版本可能以下所示:

function useReducer(reducer, initialState) {
  const [state, setState] = useState(initialState);

  function dispatch(action) {
    const nextState = reducer(state, action);
    setState(nextState);
  }

  return [state, dispatch];
}
複製代碼

如今咱們能夠在咱們的組件中使用它,讓reducer驅動它的狀態管理:

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

  function handleAddClick(text) {
    dispatch({ type: 'add', text });
  }

  // ...
}
複製代碼

在複雜組件中使用reducer管理本地狀態的需求很常見,咱們已經將useReducer Hook構建到React中。 你能夠在Hooks API參考中找到它與其餘內置Hook一塊兒使用。

相關文章
相關標籤/搜索