在React中創建自定義hook

什么是自定義hook?

使用自定義hook可以將某些組件邏輯提取到可重用的函數中。

自定義hook是一個從use開始的調用其他hook的Javascript函數。請記住,組件和hook是函數,因此我們在這里實際上并沒有創建任何新概念。我們只是將代碼重構為另一個函數以使其可重用。

不使用自定義hook

假設我們的組件中有一個功能可以檢索窗口的寬度。我們想知道用戶何時調整屏幕大小。我們可以這樣寫:

const LayoutComponent = () => {
  const [onSmallScreen, setOnSmallScreen] = useState(false);

  useEffect(() => {
    checkScreenSize();
    window.addEventListener("resize", checkScreenSize);
  }, []);

  let checkScreenSize = () => {
    setOnSmallScreen(window.innerWidth < 768);
  };

  return (
    <div className={`${onSmallScreen ? "small" : "large"}`}>
      <h1>Hello World!</h1>
    </div>
  );
};

我們這里有一個具有onSmallScreen state的組件,該組件知道我們是否在寬度小于768像素的窗口中。要知道這一點,我們使用了useEffecthook。在該hook內,我們首先調用checkScreenSize函數,該函數更新onSmallScreen狀態變量。最后,我們將checkScreenSize函數綁定到調整大小事件偵聽器,以在發生調整大小事件時在必要時更新狀態。

創建自定義hook

這樣很好。窗口的寬度一旦小于600像素,類名稱就會更改為small,而超過600像素時,類名稱會變為large。

現在,假設我想在應用程序的其他位置使用此功能,控制窗口的寬度。我應該復制并粘貼此代碼嗎?我們可以,但是我們可以在自定義hook中提取此功能,并在需要的任何地方重復使用。

因為hook只是Javascript函數,所以它們實際上不需要一個React組件。

我將創建一個名為useWindowWidth.js的新文件:

import { useState, useEffect } from "react";

const useWindowsWidth = () => {
  const [isScreenSmall, setIsScreenSmall] = useState(false);

  let checkScreenSize = () => {
    setIsScreenSmall(window.innerWidth < 600);
  };
  useEffect(() => {
    checkScreenSize();
    window.addEventListener("resize", checkScreenSize);

    return () => window.removeEventListener("resize", checkScreenSize);
  }, []);

  return isScreenSmall;
};

export default useWindowsWidth;

我們在useWindowWidth函數中提取了此功能?,F在,我們可以將其導入到要使用的任何位置!

import React from 'react'
import useWindowWidth from './useWindowWidth.js'

const MyComponent = () => {
  const onSmallScreen = useWindowWidth();

  return (
    // Return some elements
  )
}

那不是很酷嗎?我在一個項目中擁有其中一種功能。我需要知道窗口的大小才能適應我正在渲染的元素。使用自定義hook可以減少重復代碼的數量。

當然,您可以在組件的hook內部使用的所有內容都可以提取并用于自定義hook。

例如,假設您有一些組件可顯示文章的評論列表。我們可以想象其中的幾行:


const ArticleWithComments = (articleId) => {
  const [comments, setComments] = useState([])
  const [error, setError] = useState(null)

  let handleCommentsSuccessFetch = (articleComments) => setComments(articleComments)

  let handleError = error => setError(error)

  useEffect(() => {
    fetchComments(articleId, handleCommentsSuccessFetch, handleError)
  }, [])

  return (
    // Do something in the DOM
  )
}

const BlogPostWithComments = (blogPostId) => {
  const [comments, setComments] = useState([])
  const [error, setError] = useState(null)

  let handleCommentsSuccessFetch = (blogPostComments) => setComments(blogPostComments)

  let handleError = error => setError(error)

  useEffect(() => {
    fetchComments(blogPostId, handleCommentsSuccessFetch, handleError)
  }, [])

  return (
    // Do something in the DOM
  )
}

在這個例子中,我們有兩個組成部分。他們倆都根據ID(文章ID或博客文章ID)獲取評論列表。在useEffect hook中,我們有一個API調用,可通過兩個函數檢索這些注釋。一個在成功的情況下將狀態設置為注釋,第二個在錯誤的情況下將狀態設置為錯誤。

但是,功能在這兩個組件之間是重復的。幸運的是,我們可以在自定義hook中提取此功能:

const useCommentsRetriever = (entityId) => {
  const [comments, setComments] = useState([]);
  const [error, setError] = useState(null);

  let handleCommentsSuccessFetch = (comments) => setComments(comments);

  let handleError = (error) => setError(error);

  useEffect(() => {
    fetchComments(entityId, handleCommentsSuccessFetch, handleError);
  }, []);

  return [comments, error];
};

在這里,我們有了hook useCommentsRetriever。它以一個entityId作為參數。這將是我們文章的ID或博客文章的ID。然后,它類似于組件中的內容。不同之處在于此自定義hook需要返回某些內容。我選擇在這里返回一個數組。第一個元素是注釋,第二個元素是錯誤。

它將以這種方式使用:

//Import the custom hook
import useCommentsRetriever from './useCommentsRetriever.js'

const ArticleWithComments = (articleId) => {

  const [comments, error] = useCommentsRetriever(articleId)

  return (
    // Do something in the DOM
  )
}

const BlogPostWithComments = (blogPostId) => {

  const [comments, error] = useCommentsRetriever(blogPostId)

  return (
    // Do something in the DOM
  )
}

看看我們需要寫多少代碼?該useCommentsRetriever一個id作為參數。這[comments, error]就是我們所謂的數組解構。hookuseCommentsRetriever返回一個數組。我們將該數組的第一項分配給變量名注釋,將該數組的第二項分配給變量名錯誤。

請注意,我可以根據需要使用任何方式命名這些變量。我在兩個部分中也可以使用不同的名稱。因此,當您看到useStatehook中使用的語法相同時,這是因為useState hook還返回了一個數組

我們是否必須開始使用自定義hook?

根據React文檔,是的。

您可以查看文檔中的hook規則以獲取更多信息。

自定義hook的隔離特性

如果在兩個組件中使用相同的自定義hook,則它們將不會共享狀態。BlogPostWithComments中的狀態將與ArticleWithComments中的狀態完全分開。每個自定義hook都會使用React中的useStateuseEffect創建一個新函數。我們可以在同一個組件內使用多個hook,此處應用相同的邏輯。

總結

自定義hook可以讓您在編寫React代碼時真正發揮想象力。您可以采用類組件無法實現的方式提取和共享邏輯。

參考

How to create custom Hooks in React

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容