這篇文章主要介紹“自定義Hook的方法有哪些”,在日常操作中,相信很多人在自定義Hook的方法有哪些問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”自定義Hook的方法有哪些”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!
創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價比九原網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式九原網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋九原地區(qū)。費(fèi)用合理售后完善,十載實(shí)體公司更值得信賴。
React hooks
React hooks 已經(jīng)在16.8版本引入到庫中。它允許我們在函數(shù)組件中使用狀態(tài)和其他React特性,這樣我們甚至不需要再編寫類組件。
實(shí)際上,Hooks 遠(yuǎn)不止于此。
Hooks 可以將組件內(nèi)的邏輯組織成可重用的獨(dú)立單元。
Hooks 非常適合 React 組件模型和構(gòu)建應(yīng)用程序的新方法。Hooks 可以覆蓋類的所有用例,同時在整個應(yīng)用程序中提供更多的提取、測試和重用代碼的靈活性。
構(gòu)建自己的自定義React鉤子,可以輕松地在應(yīng)用程序的所有組件甚至不同應(yīng)用程序之間共享特性,這樣我們就不必重復(fù)自己的工作,從而提高構(gòu)建React應(yīng)用程序的效率。
現(xiàn)在,來看看我在開發(fā)中最常用的5個自定義鉤子,并頭開始重新創(chuàng)建它們,這樣你就能夠真正理解它們的工作方式,并確切地了解如何使用它們來提高生產(chǎn)率和加快開發(fā)過程。
我們直接開始創(chuàng)建我們的第一個自定義React Hooks。
useFetch
獲取數(shù)據(jù)是我每次創(chuàng)建React應(yīng)用時都會做的事情。我甚至在一個應(yīng)用程序中進(jìn)行了好多個這樣的重復(fù)獲取。
不管我們選擇哪種方式來獲取數(shù)據(jù),Axios、Fetch API,還是其他,我們很有可能在React組件序中一次又一次地編寫相同的代碼。
因此,我們看看如何構(gòu)建一個簡單但有用的自定義 Hook,以便在需要在應(yīng)用程序內(nèi)部獲取數(shù)據(jù)時調(diào)用該 Hook。
okk,這個 Hook 我們叫它 useFetch。
這個 Hook 接受兩個參數(shù),一個是獲取數(shù)據(jù)所需查詢的URL,另一個是表示要應(yīng)用于請求的選項(xiàng)的對象。
import { useState, useEffect } from 'react'; const useFetch = (url = '', options = null) => {}; export default useFetch;
獲取數(shù)據(jù)是一個副作用。因此,我們應(yīng)該使用useEffect Hook 來執(zhí)行查詢。
在本例中,我們使用 Fetch API來發(fā)出請求。我們會傳遞URL和 options。一旦 Promise 被解決,我們就通過解析響應(yīng)體來檢索數(shù)據(jù)。為此,我們使用json()方法。
然后,我們只需要將它存儲在一個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ò)錯誤,以防我們的請求出錯。所以我們要用另一個 state 變量來存儲錯誤。這樣我們就能從 Hook 中返回它并能夠判斷是否發(fā)生了錯誤。
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返回一個對象,其中包含從URL中獲取的數(shù)據(jù),如果發(fā)生了任何錯誤,則返回錯誤。
return { error, data };
最后,向用戶表明異步請求的狀態(tài)通常是一個好做法,比如在呈現(xiàn)結(jié)果之前顯示 loading。
因此,我們添加第三個 state 變量來跟蹤請求的狀態(tài)。在請求之前,將loading設(shè)置為true,并在請求之后完成后設(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 變量,以便在請求運(yùn)行時在組件中使用它來呈現(xiàn)一個 loading,方便用戶知道我們正在獲取他們所請求的數(shù)據(jù)。
return { loading, error, data };
在使用 userFetch 之前,我們還有一件事。
我們需要檢查使用我們 Hook 的組件是否仍然被掛載,以更新我們的狀態(tài)變量。否則,會有內(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。從那里,我們得到一個對象,我們可以使用它來渲染我們的應(yīng)用程序。
import useFetch from './useFetch'; const App = () => { const { loading, error, data = [] } = useFetch( 'https://hn.algolia.com/api/v1/search?query=react' ); if (error) returnError!
; if (loading) returnLoading...
; return (); };{data?.hits?.map(item => (
- {item.title}
))}
useEventListener
這個 Hook 負(fù)責(zé)在組件內(nèi)部設(shè)置和清理事件監(jiān)聽器。
這樣,我們就不需要每次添加事件監(jiān)聽器,做重復(fù)的工作。
這個函數(shù)有幾個參數(shù),eventType 事件類型,listener 監(jiān)聽函數(shù),target 監(jiān)聽對象,options 可選參數(shù)。
import { useEffect, useRef } from 'react'; const useEventListener = ( eventType = '', listener = () => null, target = null, options = null ) => {}; export default useEventListener;
與前一個 Hook 一樣,用 useEffect 來添加一個事件監(jiān)聽器。首先,我們需要確保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)聽器并在卸載函數(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í)際上,我們也會使用一個引用對象來存儲和持久化監(jiān)聽器函數(shù)。只有當(dāng)監(jiān)聽器函數(shù)發(fā)生變化并在事件監(jiān)聽器方法中使用該引用時,我們才會更新該引用。
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)槲覀冎皇莻陕犑录⑦\(yùn)行處理程序函數(shù)傳入作為參數(shù)。
現(xiàn)在,很容易將事件偵聽器添加到我們的組件(例如以下組件)中,以檢測DOM元素外部的點(diǎn)擊。如果用戶單擊對話框組件,則在此處關(guān)閉對話框組件。
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(, document.body ) : null; }; export default Dialog;What's up{' '} YouTube ?
useLocalStorage
這個 Hook 主要有兩個參數(shù),一個是 key,一個是 value。
import { useState } from 'react'; const useLocalStorage = (key = '', initialValue = '') => {}; export default useLocalStorage;
然后,返回一個數(shù)組,類似于使用 useState 獲得的數(shù)組。因此,此數(shù)組將包含有狀態(tài)值和在將其持久存儲在localStorage 中時對其進(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;
在這里,我們使用惰性初始化來讀取 localStorage 以獲取鍵的值,如果找到該值,則解析該值,否則返回傳入的initialValue。
如果在讀取 localStorage 時出現(xiàn)錯誤,我們只記錄一個錯誤并返回初始值。
最后,我們需要創(chuàng)建 update 函數(shù)來返回它將在localStorage 中存儲任何狀態(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ù)同時更新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 (); } export default App;Your application's settings:
useMediaQuery
這個 Hook 幫助我們在功能組件中以編程方式測試和監(jiān)控媒體查詢。這是非常有用的,例如,當(dāng)你需要渲染不同的UI取決于設(shè)備的類型或特定的特征。
我們的 Hook 接受3個參數(shù):
首先,對應(yīng)媒體查詢的字符串?dāng)?shù)組
然后,以與前一個數(shù)組相同的順序匹配這些媒體查詢的值數(shù)組
最后,如果沒有匹配的媒體查詢,則使用默認(rèn)值
import { useState, useCallback, useEffect } from 'react'; const useMediaQuery = (queries = [], values = [], defaultValue) => {}; export default useMediaQuery;
我們在這個 Hook 中做的第一件事是為每個匹配的媒體查詢構(gòu)建一個媒體查詢列表。使用這個數(shù)組通過匹配媒體查詢來獲得相應(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)建了一個包裝在useCallback 中的回調(diào)函數(shù)。檢索列表中第一個匹配的媒體查詢的值,如果沒有匹配則返回默認(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)建一個React狀態(tài)來存儲匹配的值,并使用上面定義的函數(shù)來初始化它。
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;
最后,我們在 useEffect 中添加一個事件監(jiān)聽器來監(jiān)聽每個媒體查詢的更改。當(dāng)發(fā)生變化時,我們運(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;
我最近使用的一個簡單的例子是添加一個媒體查詢來檢查設(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 (Hover me!); } export default App;
useDarkMode
這個是我的最愛。它能輕松快速地將暗模式功能應(yīng)用于任何React應(yīng)用程序。
這個 Hook 主要按需啟用和禁用暗模式,將當(dāng)前狀態(tài)存儲在localStorage 中。
為此,我們將使用我們剛剛構(gòu)建的兩個鉤子:useMediaQuery和useLocalStorage。
然后,使用“ useLocalStorage”,我們可以在localStorage中初始化,存儲和保留當(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類。這樣,我們可以簡單地將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)知識,請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!