詳解React自定義Hook
前言
在 React 項(xiàng)目中,我們經(jīng)常會(huì)使用到 React 自帶的幾個(gè)內(nèi)置 Hooks,如 useState,useContext 和useEffect。但有時(shí),我們可能希望有一個(gè)特定目的的 Hook :例如獲取數(shù)據(jù) useData,獲取連接 useConnect 等。雖然在 React 中找不到這些 Hooks,但 React 提供了非常靈活的方式讓你為自己的需求來(lái)創(chuàng)建自己的自定義 Hooks。
如何自定義 Hooks
在 React 中你必須遵循以下命名約定:
React Component: React 組件名稱(chēng)必須以大寫(xiě)字母開(kāi)頭,如 StatusBar 和 SaveButton。React組件還需要 返回 一些React知道如何渲染的東西,比如
JSX
。React Hook: Hook 名必須以 use 開(kāi)頭,后面跟著一個(gè)大寫(xiě)字母,比如 useState (內(nèi)置)或useStatus (自定義)。與 React 組件不同的是自定義 Hook 可以返回任意值。
這個(gè)命名約定確保你始終可以查看組件,并了解其狀態(tài)、效果以及其他 React 特性可能“隱藏”的位置。例如,如果你在組件中看到 getColor() 函數(shù)調(diào)用,你可以確定它不可能包含 React state,因?yàn)槠涿Q(chēng)不以u(píng)se開(kāi)頭。但是,像 useStatus() 這樣的函數(shù)調(diào)用很可能包含對(duì)其他 Hooks 的調(diào)用!
組件之間共享邏輯
The code inside them describes what they want to do rather than how to do it .
自定義 Hooks 的核心是共享組件之間的邏輯。使用自定義 Hooks 能夠減少重復(fù)的邏輯,更重要的是,自定義 Hooks 內(nèi)部的代碼描述了它們想做什么,而不是如何做。當(dāng)你將邏輯提取到自定義Hooks 中時(shí),你可以隱藏如何處理某些"外部系統(tǒng)"或?yàn)g覽器 API 的調(diào)用的細(xì)節(jié),組件的代碼表達(dá)的是你的意圖,而不是實(shí)現(xiàn)細(xì)節(jié)。 下面是一個(gè)簡(jiǎn)單的例子:
import { useState } from 'react'; function useCounter(initialValue) { const [count, setCount] = useState(initialValue); function increment() { setCount(count + 1); } return [count, increment]; }
這個(gè)自定義 Hook 叫做 useCounter
,它接受一個(gè)初始值作為參數(shù),并返回一個(gè)數(shù)組,包含當(dāng)前的計(jì)數(shù)值和一個(gè)增加計(jì)數(shù)的函數(shù)。 使用自定義 Hook 非常簡(jiǎn)單,只需要在函數(shù)組件中調(diào)用它即可。下面是一個(gè)使用 useCounter
的例子:
import React from 'react'; import useCounter from './useCounter'; function Counter() { const [count, increment] = useCounter(0); return ( <div> <p>Count: {count}</p> <button onClick={increment}>Increment</button> </div> ); }
在這個(gè)例子中,我們導(dǎo)入了 useCounter
,并在組件中調(diào)用它。我們將返回的數(shù)組解構(gòu)為 count
和 increment
,然后在組件中使用它們。
自定義 Hooks 允許你共享有狀態(tài)邏輯,而不是狀態(tài)本身
自定義 Hooks 允許共享有狀態(tài)邏輯,但不能共享狀態(tài)本身。每個(gè)對(duì) Hook 的調(diào)用都完全獨(dú)立于對(duì)同一個(gè) Hook 的其他調(diào)用。 以上面的 useCounter
為例:
import useCounter from './useCounter'; function Counter() { const [count1, increment1] = useCounter(0); const [count2, increment2] = useCounter(100); return ( <div> <p>Count1: {count1}</p> <button onClick={increment1}>Increment1</button> <p>Count2: {count2}</p> <button onClick={increment2}>Increment2</button> </div> ); }
當(dāng)我們點(diǎn)擊 Increment2
時(shí),并不會(huì)影響 count1
,因?yàn)槊恳粋€(gè) useCounter
的調(diào)用都是獨(dú)立的,其內(nèi)部狀態(tài)也是獨(dú)立的。
分類(lèi)
功能型 Hooks
以實(shí)現(xiàn)特定功能或目的,與具體業(yè)務(wù)無(wú)關(guān):
useWindowWidth
該 hook 返回窗口寬度的值。
import { useState, useEffect } from 'react'; function useWindowWidth() { const [windowWidth, setWindowWidth] = useState(window.innerWidth); useEffect(() => { const handleResize = () => setWindowWidth(window.innerWidth); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); return windowWidth; }
useLocalStorage
該 hook 允許你在本地存儲(chǔ)中存儲(chǔ)和檢索值。
import { useState } from 'react'; function useLocalStorage(key, initialValue) { const [storedValue, setStoredValue] = useState(() => { try { const item = window.localStorage.getItem(key); return item ? JSON.parse(item) : initialValue; } catch (error) { console.log(error); return initialValue; } }); const setValue = (value) => { try { setStoredValue(value); window.localStorage.setItem(key, JSON.stringify(value)); } catch (error) { console.log(error); } }; return [storedValue, setValue]; }
業(yè)務(wù)型 Hooks
useFetch
該 hook 允許你從 API 中獲取數(shù)據(jù)。
import { useState, useEffect } from 'react'; function useFetch(url) { const [data, setData] = useState(null); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { const fetchData = async () => { try { const response = await fetch(url); const json = await response.json(); setData(json); } catch (error) { setError(error); } finally { setIsLoading(false); } }; fetchData(); }, [url]); return { data, error, isLoading }; }
useModal
該 hook 允許你管理模態(tài)對(duì)話框的狀態(tài)。
//useFetch.js import {useState, useEffect} from 'react' //don't forget to give a url parameter for the function. const useFetch = (url)=>{ const [data, setData] = useState([]) const getData = async ()=>{ const response = await fetch(url) const userdata = await response.json() setData(userdata) } useEffect(()=>{ getData() },[url]) //return data that we will need in other components. return {data}; } export default useFetch;
在多個(gè) Hook 之間傳遞信息
由于 Hook 本身就是函數(shù),因此我們可以在它們之間傳遞信息。下面我們以 useUserInfo
獲取用戶(hù)信息 為例:
//useUserInfo.jsx import { useEffect,useState } from 'react' const useUserInfo = (userId) => { const [userInfo, setUserInfo] = useState({}) useEffect(() => { fetch('/user') .then(res => res.json()) .then(data => setUserInfo(data)) }, [userId]) return userInfo } //Home.jsx ... const Home = ()=>{ const [userId,setUserId] = useState('103') const useInfo = useUserInfo(userId) return ( <> <div>name:{userInfo.name}</div> <div>age:{userInfo.age}</div> ... </> ) }
我們將 用戶(hù) id 保存在 userId
狀態(tài)變量中,當(dāng)用戶(hù)進(jìn)行某一操作 setUserId
時(shí),由于 useState
為我們提供了 userId
狀態(tài)變量的最新值,因此我們可以將它作為參數(shù)傳遞給自定義的 useUserInfo
Hook:
const [userId,setUserId] = useState('103') const userInfo = useUserInfo(userId)
此時(shí),我們的 userInfo
會(huì)隨著 userId 的改變而更新。
將 event handlers 傳遞給自定義 Hooks
This section describes an experimental API that has not yet been released in a stable version of React. 本節(jié)描述了一個(gè)尚未在 React 穩(wěn)定版本中發(fā)布的 實(shí)驗(yàn)性 API。
你可能希望讓組件自定義其行為,而不是完全地將邏輯封裝 Hooks 中,我們可以通過(guò)將 event handlers
作為參數(shù)傳遞給 Hooks,下面是一個(gè)聊天室的例子: useChatRoom
接受一個(gè)服務(wù)端 url 和 roomId,當(dāng)調(diào)用這個(gè) Hook 的時(shí)候,會(huì)進(jìn)行連接,
export function useChatRoom({ serverUrl, roomId }) { useEffect(() => { const options = { serverUrl: serverUrl, roomId: roomId }; const connection = createConnection(options); connection.connect(); connection.on('message', (msg) => { showNotification('New message: ' + msg); }); return () => connection.disconnect(); }, [roomId, serverUrl]); }
假設(shè)當(dāng)連接成功時(shí),你想將此邏輯移回你的組件:
export default function ChatRoom({ roomId }) { const [serverUrl, setServerUrl] = useState('https://localhost:1234'); useChatRoom({ roomId: roomId, serverUrl: serverUrl, onReceiveMessage(msg) { showNotification('New message: ' + msg); } }); // ...
要做到這一點(diǎn),改變你的自定義 Hook ,把 onReceiveMessage
作為它的命名選項(xiàng)之一:
export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) { useEffect(() => { const options = { serverUrl: serverUrl, roomId: roomId }; const connection = createConnection(options); connection.connect(); connection.on('message', (msg) => { onReceiveMessage(msg); }); return () => connection.disconnect(); }, [roomId, serverUrl, onReceiveMessage]); // ? All dependencies declared }
這可以工作,但是當(dāng)你的自定義 Hook 接受事件處理程序時(shí),你還可以做一個(gè)改進(jìn)。 在 onReceiveMessage
上添加依賴(lài)并不理想,因?yàn)樗鼤?huì)導(dǎo)致每次組件重新渲染時(shí)聊天都重新連接。將此事件處理程序包裝到 EffectEvent
中以將其從依賴(lài)項(xiàng)中移除:
import { useEffect, useEffectEvent } from 'react'; // ... export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) { const onMessage = useEffectEvent(onReceiveMessage); useEffect(() => { const options = { serverUrl: serverUrl, roomId: roomId }; const connection = createConnection(options); connection.connect(); connection.on('message', (msg) => { onMessage(msg); }); return () => connection.disconnect(); }, [roomId, serverUrl]); // ? All dependencies declared }
現(xiàn)在不會(huì)在每次重新渲染聊天室組件時(shí)進(jìn)行重新連接。
開(kāi)源 React Hooks 庫(kù)
- ahooks 一套由阿里巴巴開(kāi)源的 React Hooks 庫(kù),封裝了大量好用的 Hooks。
- react-use 一個(gè)必不可少的 React Hooks 集合。其包含了傳感器、用戶(hù)界面、動(dòng)畫(huà)效果、副作用、生命周期、狀態(tài)這六大類(lèi)的Hooks。
- useHooks 一組易于理解的 React Hook集合。
- react-recipes 一個(gè)包含流行的自定義 Hook 的 React Hooks 實(shí)用程序庫(kù)。
- Rhooks 一組基本的 React 自定義Hooks。
- react-hanger 一組有用的 hooks,用于特定于某些基本類(lèi)型的狀態(tài)更改輔助函數(shù)。
- Beautiful React Hook 一組漂亮的(希望有用的)React hooks 來(lái)加速你的組件和 hooks 開(kāi)發(fā)。
- Awesome React Hooks 一個(gè)很棒的 React Hooks 資源集合,該集合包含React Hooks教程、視頻、工具,Hooks列表。其中Hooks列表中包含了眾多實(shí)用的自定義Hooks。
- SWR 一個(gè)用于獲取數(shù)據(jù)的 React Hooks 庫(kù)。只需一個(gè)Hook,就可以顯著簡(jiǎn)化項(xiàng)目中的數(shù)據(jù)獲取邏輯。
- React Hook Form 一個(gè)用于表單狀態(tài)管理和驗(yàn)證的 React Hooks (Web + React Native)。
總結(jié)
自定義 Hooks 可以幫助你遷移到更好的開(kāi)發(fā)范式。通過(guò)將一些通用邏輯封裝在自定義 Hooks 中,你可以使組件代碼保持簡(jiǎn)潔并專(zhuān)注于核心意圖,這有助于減少重復(fù)性的代碼,并使你的代碼更易于維護(hù)和更新,從而使你能夠更快速地開(kāi)發(fā)新功能。
對(duì)于 Effect 而言,這樣可以使數(shù)據(jù)在 Effects 中流動(dòng)的過(guò)程變得非常明確。這讓你的組件能夠專(zhuān)注于意圖,而不是 Effects 的具體實(shí)現(xiàn)。當(dāng) React 添加新功能時(shí),你可以刪除那些 Effects 而不影響任何組件。就像設(shè)計(jì)系統(tǒng)一樣,你可能會(huì)發(fā)現(xiàn)從應(yīng)用程序組件中提取常見(jiàn)習(xí)慣用法到自定義 Hooks 中是有非常幫助的。這將使你的組件代碼專(zhuān)注于意圖,并允許你避免頻繁編寫(xiě)原始 Effects,這也是 React 開(kāi)發(fā)者所推崇的。
以上就是詳解React自定義Hook的詳細(xì)內(nèi)容,更多關(guān)于React自定義Hook的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
React找不到模塊“./index.module.scss”或其相應(yīng)的類(lèi)型聲明及解決方法
這篇文章主要介紹了React找不到模塊“./index.module.scss”或其相應(yīng)的類(lèi)型聲明及解決方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09作為老司機(jī)使用 React 總結(jié)的 11 個(gè)經(jīng)驗(yàn)教訓(xùn)
這篇文章主要介紹了作為老司機(jī)使用 React 總結(jié)的 11 個(gè)經(jīng)驗(yàn)教訓(xùn),需要的朋友可以參考下2017-04-04深入理解React Native原生模塊與JS模塊通信的幾種方式
本篇文章主要介紹了深入理解React Native原生模塊與JS模塊通信的幾種方式,具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07React?Native項(xiàng)目設(shè)置路徑別名示例
這篇文章主要為大家介紹了React?Native項(xiàng)目設(shè)置路徑別名實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05從零開(kāi)始搭建一個(gè)react項(xiàng)目開(kāi)發(fā)
這篇文章主要介紹了從零開(kāi)始搭建一個(gè)react項(xiàng)目開(kāi)發(fā),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-02-02react?hooks?UI與業(yè)務(wù)邏輯分離必要性技術(shù)方案
這篇文章主要為大家介紹了react?hooks?UI與業(yè)務(wù)邏輯分離必要性技術(shù)方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11ReactNative-JS 調(diào)用原生方法實(shí)例代碼
這篇文章主要介紹了ReactNative-JS 調(diào)用原生方法實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2016-10-10