溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

自定義Hook的方法有哪些

發(fā)布時(shí)間:2021-10-18 16:54:36 來(lái)源:億速云 閱讀:193 作者:iii 欄目:web開(kāi)發(fā)

這篇文章主要介紹“自定義Hook的方法有哪些”,在日常操作中,相信很多人在自定義Hook的方法有哪些問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”自定義Hook的方法有哪些”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

React hooks

自定義Hook的方法有哪些

React hooks 已經(jīng)在16.8版本引入到庫(kù)中。它允許我們?cè)诤瘮?shù)組件中使用狀態(tài)和其他React特性,這樣我們甚至不需要再編寫類組件。

實(shí)際上,Hooks 遠(yuǎn)不止于此。

Hooks 可以將組件內(nèi)的邏輯組織成可重用的獨(dú)立單元。

Hooks 非常適合 React 組件模型和構(gòu)建應(yīng)用程序的新方法。Hooks  可以覆蓋類的所有用例,同時(shí)在整個(gè)應(yīng)用程序中提供更多的提取、測(cè)試和重用代碼的靈活性。

構(gòu)建自己的自定義React鉤子,可以輕松地在應(yīng)用程序的所有組件甚至不同應(yīng)用程序之間共享特性,這樣我們就不必重復(fù)自己的工作,從而提高構(gòu)建React應(yīng)用程序的效率。

現(xiàn)在,來(lái)看看我在開(kāi)發(fā)中最常用的5個(gè)自定義鉤子,并頭開(kāi)始重新創(chuàng)建它們,這樣你就能夠真正理解它們的工作方式,并確切地了解如何使用它們來(lái)提高生產(chǎn)率和加快開(kāi)發(fā)過(guò)程。

我們直接開(kāi)始創(chuàng)建我們的第一個(gè)自定義React Hooks。

useFetch

獲取數(shù)據(jù)是我每次創(chuàng)建React應(yīng)用時(shí)都會(huì)做的事情。我甚至在一個(gè)應(yīng)用程序中進(jìn)行了好多個(gè)這樣的重復(fù)獲取。

不管我們選擇哪種方式來(lái)獲取數(shù)據(jù),Axios、Fetch API,還是其他,我們很有可能在React組件序中一次又一次地編寫相同的代碼。

因此,我們看看如何構(gòu)建一個(gè)簡(jiǎn)單但有用的自定義 Hook,以便在需要在應(yīng)用程序內(nèi)部獲取數(shù)據(jù)時(shí)調(diào)用該 Hook。

okk,這個(gè) Hook 我們叫它 useFetch。

這個(gè) Hook 接受兩個(gè)參數(shù),一個(gè)是獲取數(shù)據(jù)所需查詢的URL,另一個(gè)是表示要應(yīng)用于請(qǐng)求的選項(xiàng)的對(duì)象。

import { useState, useEffect } from 'react';  const useFetch = (url = '', options = null) => {};  export default useFetch;

獲取數(shù)據(jù)是一個(gè)副作用。因此,我們應(yīng)該使用useEffect Hook 來(lái)執(zhí)行查詢。

在本例中,我們使用 Fetch API來(lái)發(fā)出請(qǐng)求。我們會(huì)傳遞URL和 options。一旦 Promise  被解決,我們就通過(guò)解析響應(yīng)體來(lái)檢索數(shù)據(jù)。為此,我們使用json()方法。

然后,我們只需要將它存儲(chǔ)在一個(gè)React state 變量中。

import { useState, useEffect } from 'react';  const useFetch = (url = '', options = null) => {   const [data, setData] = useState(null);    useEffect(() => {     fetch(url, options)       .then(res => res.json())       .then(data => setData(data));   }, [url, options]); };  export default useFetch;

這里,我們還需要處理網(wǎng)絡(luò)錯(cuò)誤,以防我們的請(qǐng)求出錯(cuò)。所以我們要用另一個(gè) state 變量來(lái)存儲(chǔ)錯(cuò)誤。這樣我們就能從 Hook  中返回它并能夠判斷是否發(fā)生了錯(cuò)誤。

import { useState, useEffect } from 'react';  const useFetch = (url = '', options = null) => {   const [data, setData] = useState(null);   const [error, setError] = useState(null);    useEffect(() => {     fetch(url, options)       .then(res => res.json())       .then(data => {         if (isMounted) {           setData(data);           setError(null);         }       })       .catch(error => {         if (isMounted) {           setError(error);           setData(null);         }       });   }, [url, options]); };  export default useFetch;

useFetch返回一個(gè)對(duì)象,其中包含從URL中獲取的數(shù)據(jù),如果發(fā)生了任何錯(cuò)誤,則返回錯(cuò)誤。

return { error, data };

最后,向用戶表明異步請(qǐng)求的狀態(tài)通常是一個(gè)好做法,比如在呈現(xiàn)結(jié)果之前顯示 loading。

因此,我們添加第三個(gè) state 變量來(lái)跟蹤請(qǐng)求的狀態(tài)。在請(qǐng)求之前,將loading設(shè)置為true,并在請(qǐng)求之后完成后設(shè)置為false。

const useFetch = (url = '', options = null) => {   const [data, setData] = useState(null);   const [error, setError] = useState(null);   const [loading, setLoading] = useState(false);    useEffect(() => {     setLoading(true);      fetch(url, options)       .then(res => res.json())       .then(data => {         setData(data);         setError(null);       })       .catch(error => {         setError(error);         setData(null);       })       .finally(() => setLoading(false));   }, [url, options]);    return { error, data }; };

現(xiàn)在,我們可以返回 loading 變量,以便在請(qǐng)求運(yùn)行時(shí)在組件中使用它來(lái)呈現(xiàn)一個(gè) loading,方便用戶知道我們正在獲取他們所請(qǐng)求的數(shù)據(jù)。

return { loading, error, data };

在使用 userFetch 之前,我們還有一件事。

我們需要檢查使用我們 Hook 的組件是否仍然被掛載,以更新我們的狀態(tài)變量。否則,會(huì)有內(nèi)存泄漏。

import { useState, useEffect } from 'react';  const useFetch = (url = '', options = null) => {   const [data, setData] = useState(null);   const [error, setError] = useState(null);   const [loading, setLoading] = useState(false);    useEffect(() => {     let isMounted = true;      setLoading(true);      fetch(url, options)       .then(res => res.json())       .then(data => {         if (isMounted) {           setData(data);           setError(null);         }       })       .catch(error => {         if (isMounted) {           setError(error);           setData(null);         }       })       .finally(() => isMounted && setLoading(false));      return () => (isMounted = false);   }, [url, options]);    return { loading, error, data }; };  export default useFetch;

接下就是怎么用了?

我們只需要傳遞我們想要檢索的資源的URL。從那里,我們得到一個(gè)對(duì)象,我們可以使用它來(lái)渲染我們的應(yīng)用程序。

import useFetch from './useFetch';  const App = () => {   const { loading, error, data = [] } = useFetch(     'https://hn.algolia.com/api/v1/search?query=react'   );    if (error) return <p>Error!</p>;   if (loading) return <p>Loading...</p>;    return (     <div>       <ul>         {data?.hits?.map(item => (           <li key={item.objectID}>             <a href={item.url}>{item.title}</a>           </li>         ))}       </ul>     </div>   ); };

useEventListener

這個(gè) Hook 負(fù)責(zé)在組件內(nèi)部設(shè)置和清理事件監(jiān)聽(tīng)器。

這樣,我們就不需要每次添加事件監(jiān)聽(tīng)器,做重復(fù)的工作。

這個(gè)函數(shù)有幾個(gè)參數(shù),eventType 事件類型,listener 監(jiān)聽(tīng)函數(shù),target 監(jiān)聽(tīng)對(duì)象,options 可選參數(shù)。

import { useEffect, useRef } from 'react';  const useEventListener = (   eventType = '',   listener = () => null,   target = null,   options = null ) => {};  export default useEventListener;

與前一個(gè) Hook 一樣,用 useEffect 來(lái)添加一個(gè)事件監(jiān)聽(tīng)器。首先,我們需要確保target  是否支持addEventListener方法。否則,我們什么也不做。

import { useEffect, useRef } from 'react';  const useEventListener = (   eventType = '',   listener = () => null,   target = null,   options = null ) => {    useEffect(() => {     if (!target?.addEventListener) return;   }, [target]); };  export default useEventListener;

然后,我們可以添加實(shí)際的事件監(jiān)聽(tīng)器并在卸載函數(shù)中刪除它。

import { useEffect, useRef } from 'react';  const useEventListener = (   eventType = '',   listener = () => null,   target = null,   options = null ) => {   useEffect(() => {     if (!target?.addEventListener) return;      target.addEventListener(eventType, listener, options);      return () => {       target.removeEventListener(eventType, listener, options);     };   }, [eventType, target, options, listener]); };  export default useEventListener;

實(shí)際上,我們也會(huì)使用一個(gè)引用對(duì)象來(lái)存儲(chǔ)和持久化監(jiān)聽(tīng)器函數(shù)。只有當(dāng)監(jiān)聽(tīng)器函數(shù)發(fā)生變化并在事件監(jiān)聽(tīng)器方法中使用該引用時(shí),我們才會(huì)更新該引用。

import { useEffect, useRef } from 'react';  const useEventListener = (   eventType = '',   listener = () => null,   target = null,   options = null ) => {   const savedListener = useRef();    useEffect(() => {     savedListener.current = listener;   }, [listener]);    useEffect(() => {     if (!target?.addEventListener) return;      const eventListener = event => savedListener.current(event);      target.addEventListener(eventType, eventListener, options);      return () => {       target.removeEventListener(eventType, eventListener, options);     };   }, [eventType, target, options]); };  export default useEventListener;

我們不需要從此 Hook 返回任何內(nèi)容,因?yàn)槲覀冎皇莻陕?tīng)事件并運(yùn)行處理程序函數(shù)傳入作為參數(shù)。

現(xiàn)在,很容易將事件偵聽(tīng)器添加到我們的組件(例如以下組件)中,以檢測(cè)DOM元素外部的點(diǎn)擊。如果用戶單擊對(duì)話框組件,則在此處關(guān)閉對(duì)話框組件。

import { useRef } from 'react'; import ReactDOM from 'react-dom'; import { useEventListener } from './hooks';  const Dialog = ({ show = false, onClose = () => null }) => {   const dialogRef = useRef();    // Event listener to close dialog on click outside element   useEventListener(     'mousedown',     event => {       if (event.defaultPrevented) {         return; // Do nothing if the event was already processed       }       if (dialogRef.current && !dialogRef.current.contains(event.target)) {         console.log('Click outside detected -> closing dialog...');         onClose();       }     },     window   );    return show     ? ReactDOM.createPortal(         <div className="fixed inset-0 z-9999 flex items-center justify-center p-4 md:p-12 bg-blurred">           <div             className="relative bg-white rounded-md shadow-card max-h-full max-w-screen-sm w-full animate-zoom-in px-6 py-20"             ref={dialogRef}           >             <p className="text-center font-semibold text-4xl">               What's up{' '}               <span className="text-white bg-red-500 py-1 px-3 rounded-md mr-1">                 YouTube               </span>               ?             </p>           </div>         </div>,         document.body       )     : null; };  export default Dialog;

useLocalStorage

這個(gè) Hook 主要有兩個(gè)參數(shù),一個(gè)是 key,一個(gè)是 value。

import { useState } from 'react';  const useLocalStorage = (key = '', initialValue = '') => {};  export default useLocalStorage;

然后,返回一個(gè)數(shù)組,類似于使用 useState 獲得的數(shù)組。因此,此數(shù)組將包含有狀態(tài)值和在將其持久存儲(chǔ)在localStorage  中時(shí)對(duì)其進(jìn)行更新的函數(shù)。

首先,我們創(chuàng)建將與 localStorage 同步的React狀態(tài)變量。

import { useState } from 'react';  const useLocalStorage = (key = '', initialValue = '') => {   const [state, setState] = useState(() => {     try {       const item = window.localStorage.getItem(key);       return item ? JSON.parse(item) : initialValue;     } catch (error) {       console.log(error);       return initialValue;     }   }); };  export default useLocalStorage;

在這里,我們使用惰性初始化來(lái)讀取 localStorage 以獲取鍵的值,如果找到該值,則解析該值,否則返回傳入的initialValue。

如果在讀取 localStorage 時(shí)出現(xiàn)錯(cuò)誤,我們只記錄一個(gè)錯(cuò)誤并返回初始值。

最后,我們需要?jiǎng)?chuàng)建 update 函數(shù)來(lái)返回它將在localStorage 中存儲(chǔ)任何狀態(tài)的更新,而不是使用useState 返回的默認(rèn)更新。

import { useState } from 'react';  const useLocalStorage = (key = '', initialValue = '') => {   const [state, setState] = useState(() => {     try {       const item = window.localStorage.getItem(key);       return item ? JSON.parse(item) : initialValue;     } catch (error) {       return initialValue;     }   });    const setLocalStorageState = newState => {     try {       const newStateValue =         typeof newState === 'function' ? newState(state) : newState;       setState(newStateValue);       window.localStorage.setItem(key, JSON.stringify(newStateValue));     } catch (error) {       console.error(`Unable to store new value for ${key} in localStorage.`);     }   };    return [state, setLocalStorageState]; };  export default useLocalStorage;

此函數(shù)同時(shí)更新React狀態(tài)和 localStorage 中的相應(yīng)鍵/值。這里,我們還可以支持函數(shù)更新,例如常規(guī)的useState hook。

最后,我們返回狀態(tài)值和我們的自定義更新函數(shù)。

現(xiàn)在可以使用useLocalStorage hook 將組件中的任何數(shù)據(jù)持久化到localStorage中。

import { useLocalStorage } from './hooks';  const defaultSettings = {   notifications: 'weekly', };  function App() {   const [appSettings, setAppSettings] = useLocalStorage(     'app-settings',     defaultSettings   );    return (     <div className="h-full w-full flex flex-col justify-center items-center">       <div className="flex items-center mb-8">         <p className="font-medium text-lg mr-4">Your application's settings:</p>          <select           value={appSettings.notifications}           onChange={e =>             setAppSettings(settings => ({               ...settings,               notifications: e.target.value,             }))           }           className="border border-gray-900 rounded py-2 px-4 "         >           <option value="daily">daily</option>           <option value="weekly">weekly</option>           <option value="monthly">monthly</option>         </select>       </div>        <button         onClick={() => setAppSettings(defaultSettings)}         className="rounded-md shadow-md py-2 px-6 bg-red-500 text-white uppercase font-medium tracking-wide text-sm leading-8"       >         Reset settings       </button>     </div>   ); }  export default App;

 useMediaQuery

這個(gè) Hook  幫助我們?cè)诠δ芙M件中以編程方式測(cè)試和監(jiān)控媒體查詢。這是非常有用的,例如,當(dāng)你需要渲染不同的UI取決于設(shè)備的類型或特定的特征。

我們的 Hook 接受3個(gè)參數(shù):

  • 首先,對(duì)應(yīng)媒體查詢的字符串?dāng)?shù)組

  • 然后,以與前一個(gè)數(shù)組相同的順序匹配這些媒體查詢的值數(shù)組

  • 最后,如果沒(méi)有匹配的媒體查詢,則使用默認(rèn)值

import { useState, useCallback, useEffect } from 'react';  const useMediaQuery = (queries = [], values = [], defaultValue) => {};  export default useMediaQuery;

我們?cè)谶@個(gè) Hook 中做的第一件事是為每個(gè)匹配的媒體查詢構(gòu)建一個(gè)媒體查詢列表。使用這個(gè)數(shù)組通過(guò)匹配媒體查詢來(lái)獲得相應(yīng)的值。

import { useState, useCallback, useEffect } from 'react';  const useMediaQuery = (queries = [], values = [], defaultValue) => {   const mediaQueryList = queries.map(q => window.matchMedia(q)); };  export default useMediaQuery;

為此,我們創(chuàng)建了一個(gè)包裝在useCallback 中的回調(diào)函數(shù)。檢索列表中第一個(gè)匹配的媒體查詢的值,如果沒(méi)有匹配則返回默認(rèn)值。

import { useState, useCallback, useEffect } from 'react';  const useMediaQuery = (queries = [], values = [], defaultValue) => {   const mediaQueryList = queries.map(q => window.matchMedia(q));    const getValue = useCallback(() => {     const index = mediaQueryList.findIndex(mql => mql.matches);     return typeof values[index] !== 'undefined' ? values[index] : defaultValue;   }, [mediaQueryList, values, defaultValue]); };  export default useMediaQuery;

然后,我們創(chuàng)建一個(gè)React狀態(tài)來(lái)存儲(chǔ)匹配的值,并使用上面定義的函數(shù)來(lái)初始化它。

import { useState, useCallback, useEffect } from 'react';  const useMediaQuery = (queries = [], values = [], defaultValue) => {   const mediaQueryList = queries.map(q => window.matchMedia(q));    const getValue = useCallback(() => {     const index = mediaQueryList.findIndex(mql => mql.matches);     return typeof values[index] !== 'undefined' ? values[index] : defaultValue;   }, [mediaQueryList, values, defaultValue]);    const [value, setValue] = useState(getValue); };  export default useMediaQuery;

最后,我們?cè)?useEffect 中添加一個(gè)事件監(jiān)聽(tīng)器來(lái)監(jiān)聽(tīng)每個(gè)媒體查詢的更改。當(dāng)發(fā)生變化時(shí),我們運(yùn)行更新函數(shù)。

mport { useState, useCallback, useEffect } from 'react';  const useMediaQuery = (queries = [], values = [], defaultValue) => {   const mediaQueryList = queries.map(q => window.matchMedia(q));    const getValue = useCallback(() => {     const index = mediaQueryList.findIndex(mql => mql.matches);     return typeof values[index] !== 'undefined' ? values[index] : defaultValue;   }, [mediaQueryList, values, defaultValue]);    const [value, setValue] = useState(getValue);    useEffect(() => {     const handler = () => setValue(getValue);     mediaQueryList.forEach(mql => mql.addEventListener('change', handler));      return () =>       mediaQueryList.forEach(mql => mql.removeEventListener('change', handler));   }, [getValue, mediaQueryList]);    return value; };  export default useMediaQuery;

我最近使用的一個(gè)簡(jiǎn)單的例子是添加一個(gè)媒體查詢來(lái)檢查設(shè)備是否允許用戶懸停在元素上。這樣,如果用戶可以懸?;驊?yīng)用基本樣式,我就可以添加特定的不透明樣式。

import { useMediaQuery } from './hooks';  function App() {   const canHover = useMediaQuery(     // Media queries     ['(hover: hover)'],     // Values corresponding to the above media queries by array index     [true],     // Default value     false   );    const canHoverClass = 'opacity-0 hover:opacity-100 transition-opacity';   const defaultClass = 'opacity-100';    return (     <div className={canHover ? canHoverClass : defaultClass}>Hover me!</div>   ); }  export default App;

useDarkMode

這個(gè)是我的最愛(ài)。它能輕松快速地將暗模式功能應(yīng)用于任何React應(yīng)用程序。

自定義Hook的方法有哪些

這個(gè) Hook 主要按需啟用和禁用暗模式,將當(dāng)前狀態(tài)存儲(chǔ)在localStorage 中。

為此,我們將使用我們剛剛構(gòu)建的兩個(gè)鉤子:useMediaQuery和useLocalStorage。

然后,使用“ useLocalStorage”,我們可以在localStorage中初始化,存儲(chǔ)和保留當(dāng)前狀態(tài)(暗或亮模式)。

import { useEffect } from 'react'; import useMediaQuery from './useMediaQuery'; import useLocalStorage from './useLocalStorage';  const useDarkMode = () => {   const preferDarkMode = useMediaQuery(     ['(prefers-color-scheme: dark)'],     [true],     false   ); };  export default useDarkMode;

最后一部分是觸發(fā)副作用,以向document.body元素添加或刪除dark類。這樣,我們可以簡(jiǎn)單地將dark樣式應(yīng)用于我們的應(yīng)用程序。

import { useEffect } from 'react'; import useMediaQuery from './useMediaQuery'; import useLocalStorage from './useLocalStorage';  const useDarkMode = () => {   const preferDarkMode = useMediaQuery(     ['(prefers-color-scheme: dark)'],     [true],     false   );    const [enabled, setEnabled] = useLocalStorage('dark-mode', preferDarkMode);    useEffect(() => {     if (enabled) {       document.body.classList.add('dark');     } else {       document.body.classList.remove('dark');     }   }, [enabled]);    return [enabled, setEnabled]; };  export default useDarkMode;

到此,關(guān)于“自定義Hook的方法有哪些”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI