15 个实用的 React 自定义 Hook,可用于任何项目
React 自定义 hooks 允许在函数式组件中复用逻辑,从而可以分离组件,保持各个部分精简,并专注于其预期用途。自定义 hooks 还可以更轻松地测试和理解组件的逻辑,因为它可以与组件本身隔离并单独测试。自定义 hooks 还可以简化不同组件之间的逻辑共享,减少代码重复,并更轻松地维护和更新代码库。
React Hooks 的可能性无限,但并非所有 Hooks 都生而平等。在本文中,我将介绍 30 个强大的自定义 Hooks,它们可以在任何项目中使用,并且实现起来也非常快捷。
注意:这些钩子是由 Youtuber Web Dev Simplified创建的。如果您需要更深入的视频讲解,可以观看他的视频。但是,如果您只想阅读一下,请继续阅读。
目录
- useToggle
- 使用超时
- 使用Debounce
- useUpdateEffect
- 使用数组
- 使用上一个
- useStateWithHistory
- 使用存储
- useAsync
- useFetch
- useScript
- 使用DeepCompareEffect
- 使用事件监听器
- 使用屏幕
- 使用窗口大小
1. useToggle
import { useState } from "react"
export default function useToggle(defaultValue) {
const [value, setValue] = useState(defaultValue)
function toggleValue(value) {
setValue(currentValue =>
typeof value === "boolean" ? value : !currentValue
)
}
return [value, toggleValue]
}
useToggle
是一个自定义的 React Hook,允许组件在 true 和 false 之间切换值。它使用useState
Hook 来管理组件的状态。首先,Hook 接受一个defaultValue
参数来初始化值状态。然后,它返回一个包含两个元素的数组:当前值和一个toggleValue
在 true 和 false 之间切换值的函数。该函数接受一个参数。如果参数是布尔值,则将值设置为该参数的值。否则,它将切换当前值。
以下是如何使用此钩子的示例:
import useToggle from "./useToggle"
export default function ToggleComponent() {
const [value, toggleValue] = useToggle(false)
return (
<div>
<div>{value.toString()}</div>
<button onClick={toggleValue}>Toggle</button>
<button onClick={() => toggleValue(true)}>Make True</button>
<button onClick={() => toggleValue(false)}>Make False</button>
</div>
)
}
2. useTimeout
import { useCallback, useEffect, useRef } from "react"
export default function useTimeout(callback, delay) {
const callbackRef = useRef(callback)
const timeoutRef = useRef()
useEffect(() => {
callbackRef.current = callback
}, [callback])
const set = useCallback(() => {
timeoutRef.current = setTimeout(() => callbackRef.current(), delay)
}, [delay])
const clear = useCallback(() => {
timeoutRef.current && clearTimeout(timeoutRef.current)
}, [])
useEffect(() => {
set()
return clear
}, [delay, set, clear])
const reset = useCallback(() => {
clear()
set()
}, [clear, set])
return { reset, clear }
}
useTimeout
是一个自定义的 React 钩子,允许组件执行set
并clear
设置超时。它使用了ReactuseCallback
库中的、useEffect
和useRef
钩子。该钩子接受两个参数:一个将在指定延迟时间后调用的回调函数,以及一个延迟时间(以毫秒为单位),该延迟时间表示在调用回调函数之前应经过的时间。
该钩子返回一个具有两个属性的对象:reset
和clear
,可用于重置或清除超时的函数。
该钩子使用useRef
钩子创建两个引用:callbackRef
和timeoutRef
。将回调callbackRef
函数保存为可变值,并包含setTimeout()函数返回的超时id。timeoutRef
useEffect hook 用于确保回调函数 callbackRef.current始终传递最新的回调。
该函数使用setTimeoutset
创建一个新的超时时间,并在指定的延迟时间后调用回调函数。clear函数使用clearTimeout清除超时时间。此外,还有一个hook 用于在挂载时设置超时时间,并在卸载时移除超时时间。reset 函数是和函数的组合。最后,该hook 确保仅当依赖关系发生变化时才会重新创建函数。useEffect
clear
set
useCallback
以下是如何使用此钩子的示例:
import { useState } from "react"
import useTimeout from "./useTimeout"
export default function TimeoutComponent() {
const [count, setCount] = useState(10)
const { clear, reset } = useTimeout(() => setCount(0), 1000)
return (
<div>
<div>{count}</div>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
<button onClick={clear}>Clear Timeout</button>
<button onClick={reset}>Reset Timeout</button>
</div>
)
}
这个自定义useTimeout
钩子在组件需要充当不可避免的延迟的各种情况下非常有用。例如:
- 一段时间后消失的通知消息
- 表单提交在重定向之前会显示一段时间的加载旋转图标
- 一定时间后自动前进到下一张幻灯片的幻灯片
- 倒计时器显示剩余时间,并在达到零时触发操作
- 自动保存功能可在一定时间后保存表单数据
- 会话超时,在一定时间不活动后将用户注销
- 防抖动函数会将回调执行延迟一段时间。
它可以用于任何需要等待一定时间后再采取行动或需要延迟多次重复某个动作的情况。
3. 使用Debounce
import { useEffect } from "react"
import useTimeout from "../2-useTimeout/useTimeout"
export default function useDebounce(callback, delay, dependencies) {
const { reset, clear } = useTimeout(callback, delay)
useEffect(reset, [...dependencies, reset])
useEffect(clear, [])
}
useDebounce
useEffect
是一个自定义的 React hook,允许组件将回调函数的执行延迟指定的时间。它使用了React 库中的内置hook 和useTimeout
(第二个自定义 hook)自定义 hook。
该钩子接受三个参数:
- “callback” 是需要去抖动的函数。
- “延迟”是调用回调之前应经过的时间(以毫秒为单位)。
- “dependencies” 是一个值数组,钩子应该监听这些值的变化,如果有任何变化则重新运行回调。
该钩子使用useTimeout
钩子创建一个超时函数,该函数将在指定的延迟后调用回调useEffect
函数。该钩子用于在挂载时设置超时,并在卸载时清除超时。第一个钩子useEffect
会在依赖项发生更改时调用reset函数,第二个useEffect会在组件卸载时调用 clear 函数。
以下是如何使用此钩子的示例:
import { useState } from "react"
import useDebounce from "./useDebounce"
export default function DebounceComponent() {
const [count, setCount] = useState(10)
useDebounce(() => alert(count), 1000, [count])
return (
<div>
<div>{count}</div>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
</div>
)
}
当您想要限制短时间内回调函数的调用次数时,此钩子会非常有用。例如,当您有一个输入字段,每次用户击键都会向服务器发送搜索请求时,您应该等待用户停止输入后再发送请求,以避免不必要的网络流量并提升用户体验。
4. useUpdateEffect
import { useEffect, useRef } from "react"
export default function useUpdateEffect(callback, dependencies) {
const firstRenderRef = useRef(true)
useEffect(() => {
if (firstRenderRef.current) {
firstRenderRef.current = false
return
}
return callback()
}, dependencies)
}
useUpdateEffect
是一个自定义的 React hook,允许组件仅在特定依赖项发生变化时运行回调函数。它使用了 React 库的内置useEffect
hooks useRef
。
该钩子接受两个参数:
callback
是依赖项发生改变时应该调用的函数dependencies
是钩子应该监听变化的值数组。
该钩子使用useRef
钩子创建一个firstRenderRef
初始值为true的引用。此引用将用于跟踪组件的首次渲染。useEffect
钩子用于监听依赖项数组的变化并调用回调函数。在函数内部,它通过检查值来判断这是否是组件的首次渲染。如果是,则将其设置为 false 并返回。如果不是,则表示这是一次更新,因此它将调用回调函数并返回回调函数。useEffect
firstRenderRef
以下是如何使用此钩子的示例:
import { useState } from "react"
import useUpdateEffect from "./useUpdateEffect"
export default function UpdateEffectComponent() {
const [count, setCount] = useState(10)
useUpdateEffect(() => alert(count), [count])
return (
<div>
<div>{count}</div>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
</div>
)
}
如果您希望仅在特定值更改时运行某些逻辑,而不是在初始渲染时运行,那么此钩子会非常有用。例如,当您想在用户从下拉菜单中选择特定选项后从 API 获取数据,或者当您想在窗口大小更改后更新屏幕上元素的位置时。
5. useArray
import { useState } from "react"
export default function useArray(defaultValue) {
const [array, setArray] = useState(defaultValue)
function push(element) {
setArray(a => [...a, element])
}
function filter(callback) {
setArray(a => a.filter(callback))
}
function update(index, newElement) {
setArray(a => [
...a.slice(0, index),
newElement,
...a.slice(index + 1, a.length),
])
}
function remove(index) {
setArray(a => [...a.slice(0, index), ...a.slice(index + 1, a.length)])
}
function clear() {
setArray([])
}
return { array, set: setArray, push, filter, update, remove, clear }
}
useArray
是一个自定义的 React hook,允许组件管理数组状态。它使用 React 库中的内置useState
hook。该 hook 接受一个参数defaultValue
,用于初始化数组状态。它返回一个具有以下属性的对象:
array
是当前数组状态set
是一个允许你将数组状态设置为新值的函数push
是一个函数,可以让你向数组末尾添加一个元素filter
是一个允许你通过传递回调函数来过滤数组的函数update
是一个函数,可以让你更新数组中特定索引处的元素remove
是一个函数,允许你删除数组中特定索引处的元素clear
是一个可以让你清除数组的函数。
所有改变数组状态的函数都使用 setArray 函数。尽管如此,它们仍然以一种保持状态不变的方式执行:创建一个新数组,添加或删除元素,然后将其传递给 setArray 函数。
以下是如何使用此钩子的示例:
import useArray from "./useArray"
export default function ArrayComponent() {
const { array, set, push, remove, filter, update, clear } = useArray([
1, 2, 3, 4, 5, 6,
])
return (
<div>
<div>{array.join(", ")}</div>
<button onClick={() => push(7)}>Add 7</button>
<button onClick={() => update(1, 9)}>Change Second Element To 9</button>
<button onClick={() => remove(1)}>Remove Second Element</button>
<button onClick={() => filter(n => n < 3)}>
Keep Numbers Less Than 4
</button>
<button onClick={() => set([1, 2])}>Set To 1, 2</button>
<button onClick={clear}>Clear</button>
</div>
)
}
当您想要管理组件状态中的数据数组并执行日常数组操作(例如添加、删除、更新和过滤元素)时,此钩子会很有用。
6. usePrevious
import { useRef } from "react"
export default function usePrevious(value) {
const currentRef = useRef(value)
const previousRef = useRef()
if (currentRef.current !== value) {
previousRef.current = currentRef.current
currentRef.current = value
}
return previousRef.current
}
usePrevious
是一个自定义的 React hook,允许组件跟踪变量的先前值。它使用了React库中的内置useRef
hook 。
该钩子接受一个参数value
,即变量的当前值。然后,它创建两个引用,一个名为currentRef
,用于保存变量的当前值;另一个名为previousRef
,用于保存变量的先前值。
该钩子会将当前值与先前的值进行比较。如果不同,则用当前值更新 previousRef,用新值更新 currentRef。然后返回previousRef.current
。
以下是如何使用此钩子的示例:
import { useState } from "react"
import usePrevious from "./usePrevious"
export default function PreviousComponent() {
const [count, setCount] = useState(0)
const [name, setName] = useState("Kyle")
const previousCount = usePrevious(count)
return (
<div>
<div>
{count} - {previousCount}
</div>
<div>{name}</div>
<button onClick={() => setCount(currentCount => currentCount + 1)}>
Increment
</button>
<button onClick={() => setName("John")}>Change Name</button>
</div>
)
}
当您需要访问变量的先前值时,此钩子会很有用,例如,当您想将当前值与先前的值进行比较以检查它是否已更改,或者当您想要跟踪变量随时间的变化时。
7. useStateWithHistory
import { useCallback, useRef, useState } from "react"
export default function useStateWithHistory(
defaultValue,
{ capacity = 10 } = {}
) {
const [value, setValue] = useState(defaultValue)
const historyRef = useRef([value])
const pointerRef = useRef(0)
const set = useCallback(
v => {
const resolvedValue = typeof v === "function" ? v(value) : v
if (historyRef.current[pointerRef.current] !== resolvedValue) {
if (pointerRef.current < historyRef.current.length - 1) {
historyRef.current.splice(pointerRef.current + 1)
}
historyRef.current.push(resolvedValue)
while (historyRef.current.length > capacity) {
historyRef.current.shift()
}
pointerRef.current = historyRef.current.length - 1
}
setValue(resolvedValue)
},
[capacity, value]
)
const back = useCallback(() => {
if (pointerRef.current <= 0) return
pointerRef.current--
setValue(historyRef.current[pointerRef.current])
}, [])
const forward = useCallback(() => {
if (pointerRef.current >= historyRef.current.length - 1) return
pointerRef.current++
setValue(historyRef.current[pointerRef.current])
}, [])
const go = useCallback(index => {
if (index < 0 || index > historyRef.current.length - 1) return
pointerRef.current = index
setValue(historyRef.current[pointerRef.current])
}, [])
return [
value,
set,
{
history: historyRef.current,
pointer: pointerRef.current,
back,
forward,
go,
},
]
}
useStateWithHistory
是一个自定义的 React hook,允许组件跟踪状态的历史记录。它使用了React 库中内置的useState
、useCallback
和钩子。useRef
该钩子接受两个参数:
defaultValue
是状态的初始值capacity
是一个可选参数,用于设置历史记录中应存储的最大状态数。
该钩子创建了两个引用,一个名为 ,historyRef
用于保存状态历史记录的数组;另一个名为pointerRef
,用于保存历史记录的当前指针。它还创建了三个回调函数:set
、back
和forward
。
该set
函数用于设置状态,其工作原理与内置setState
函数类似,但它还通过向历史记录数组添加新值并更新 pointerRef 来跟踪状态的历史记录。该函数可以接受一个值或一个接收当前状态作为参数的回调函数。该函数还会通过删除最旧的元素来确保历史记录数组的容量不会超出。
该back
函数导航到历史记录中的先前状态。它减少pointerRef 的值,并使用历史记录数组中先前的值更新状态。
该forward
函数导航到历史记录中的下一个状态。它增加 pointerRef 的值,并使用历史记录数组中的下一个值更新状态。
该go
函数导航到历史记录中的特定状态。它将 pointerRef 设置为作为参数传递的索引,并使用历史记录数组中该索引处的值更新状态。
该钩子返回一个包含两个元素的数组:
- 当前状态值
set
包含历史数组、指针以及函数、back
、forward
和 的对象go
。
以下是如何使用此钩子的示例:
import { useState } from "react"
import useStateWithHistory from "./useStateWithHistory"
export default function StateWithHistoryComponent() {
const [count, setCount, { history, pointer, back, forward, go }] =
useStateWithHistory(1)
const [name, setName] = useState("Kyle")
return (
<div>
<div>{count}</div>
<div>{history.join(", ")}</div>
<div>Pointer - {pointer}</div>
<div>{name}</div>
<button onClick={() => setCount(currentCount => currentCount * 2)}>
Double
</button>
<button onClick={() => setCount(currentCount => currentCount + 1)}>
Increment
</button>
<button onClick={back}>Back</button>
<button onClick={forward}>Forward</button>
<button onClick={() => go(2)}>Go To Index 2</button>
<button onClick={() => setName("John")}>Change Name</button>
</div>
)
}
当您想要跟踪状态历史时,此钩子会很有用,例如,当您想要实现撤消或重做功能或允许用户浏览更改历史时。
8. useStorage
import { useCallback, useState, useEffect } from "react"
export function useLocalStorage(key, defaultValue) {
return useStorage(key, defaultValue, window.localStorage)
}
export function useSessionStorage(key, defaultValue) {
return useStorage(key, defaultValue, window.sessionStorage)
}
function useStorage(key, defaultValue, storageObject) {
const [value, setValue] = useState(() => {
const jsonValue = storageObject.getItem(key)
if (jsonValue != null) return JSON.parse(jsonValue)
if (typeof defaultValue === "function") {
return defaultValue()
} else {
return defaultValue
}
})
useEffect(() => {
if (value === undefined) return storageObject.removeItem(key)
storageObject.setItem(key, JSON.stringify(value))
}, [key, value, storageObject])
const remove = useCallback(() => {
setValue(undefined)
}, [])
return [value, setValue, remove]
}
useLocalStorage
and是一个自定义的useSessionStorage
React hook,允许组件将值存储在浏览器的LocalStorage或SessionStorage中,并使其与组件的状态保持同步。它使用了React 库中的内置hooks 和hooks。useState
useEffect
useCallback
useLocalStorage
和函数useSessionStorage
类似,但分别使用不同的存储方式: localStorage和sessionStorage。它们接受两个参数:key
和defaultValue
。key
是用于将值存储在存储对象中的键,defaultValue
是当存储对象中找不到该键时将使用的值。
两个函数都使用该storage
函数,该函数接受三个参数:key
、、defaultValue
和storageObject
,并返回一个包含三个元素的数组:
- 当前值
- 函数“setValue”可用于更新状态和存储中的值。
- 函数“remove”可用于从状态和存储中删除值。
useEffect钩子使存储在浏览器存储中的值与组件的状态保持同步。 该函数使用JSON.stringify()和JSON.parse方法,在将值存储到存储对象时将其转换为JSON字符串,并在从存储对象中检索时将其转换回 JavaScript 对象。这使得该钩子可以处理任何数据,而不仅仅是字符串。useStorage
每当key、value或storageObject发生变化时,钩子useEffect
就会运行。首先,它会检查值是否未定义。如果未定义,它会从存储对象中移除该项目。否则,它会将值存储在存储对象中。
以下是如何使用此钩子的示例:
import { useSessionStorage, useLocalStorage } from "./useStorage"
export default function StorageComponent() {
const [name, setName, removeName] = useSessionStorage("name", "Kyle")
const [age, setAge, removeAge] = useLocalStorage("age", 26)
return (
<div>
<div>
{name} - {age}
</div>
<button onClick={() => setName("John")}>Set Name</button>
<button onClick={() => setAge(40)}>Set Age</button>
<button onClick={removeName}>Remove Name</button>
<button onClick={removeAge}>Remove Age</button>
</div>
)
}
如果您希望跨浏览器会话或页面持久化数据,并使数据与组件状态保持同步,此钩子会非常有用。例如,您可以存储用户的设置、表单的数据或待办事项列表。使用 useLocalStorage 和 useSessionStorage 钩子,您可以根据需要灵活地使用浏览器的本地存储或会话存储。
9. useAsync
import { useCallback, useEffect, useState } from "react"
export default function useAsync(callback, dependencies = []) {
const [loading, setLoading] = useState(true)
const [error, setError] = useState()
const [value, setValue] = useState()
const callbackMemoized = useCallback(() => {
setLoading(true)
setError(undefined)
setValue(undefined)
callback()
.then(setValue)
.catch(setError)
.finally(() => setLoading(false))
}, dependencies)
useEffect(() => {
callbackMemoized()
}, [callbackMemoized])
return { loading, error, value }
}
useAsync
是一个自定义的 React 钩子,允许组件处理异步操作并跟踪加载、错误和值状态。它使用了React 库中的内置useState
钩子和钩子。useEffect
useCallback
该钩子接受两个参数:
callback
是一个返回 Promise 的函数。该函数负责执行异步操作。dependencies
是钩子需要监听的依赖项变化的数组。当任何依赖项发生变化时,回调函数都会被执行。
该钩子创建了三个状态变量:loading、error和value。loading状态用于指示异步操作当前是否正在进行中;error状态用于在 Promise 被拒绝的情况下存储错误对象;value状态用于在 Promise 被满足的情况下存储已解析的值。
该钩子还创建了一个名为callbackMemoized
using的回调函数useCallback
。此函数将loading、error和value状态设置为其初始值,然后调用传入的回调函数。当依赖项发生变化时,
该useEffect
钩子会调用callbackMemoized函数。
以下是如何使用此钩子的示例:
import useAsync from "./useAsync"
export default function AsyncComponent() {
const { loading, error, value } = useAsync(() => {
return new Promise((resolve, reject) => {
const success = false
setTimeout(() => {
success ? resolve("Hi") : reject("Error")
}, 1000)
})
})
return (
<div>
<div>Loading: {loading.toString()}</div>
<div>{error}</div>
<div>{value}</div>
</div>
)
}
当您需要处理异步操作(例如从 API 获取数据、上传文件或将数据保存到数据库)时,此钩子非常有用。它提供了一种简单的方法来管理组件中的加载、错误和值状态,并且还允许组件在某些值发生变化时重新运行异步操作。
10. useFetch
import useAsync from "../9-useAsync/useAsync"
const DEFAULT_OPTIONS = {
headers: { "Content-Type": "application/json" },
}
export default function useFetch(url, options = {}, dependencies = []) {
return useAsync(() => {
return fetch(url, { ...DEFAULT_OPTIONS, ...options }).then(res => {
if (res.ok) return res.json()
return res.json().then(json => Promise.reject(json))
})
}, dependencies)
}
useFetch
是一个自定义的 React hook,允许组件处理从 URL 获取数据,并跟踪加载、错误和值的状态。它使用内置的fetch API 和自定义 hook useAsync
,允许组件处理异步操作,并跟踪加载、错误和值的状态。
该钩子接受三个参数:
- URL是要从中获取数据的端点的 URL
- options是一个对象,包含获取请求的标头、方法和正文等选项。
- dependency是钩子需要监听的依赖项数组。当任何依赖项发生变化时,回调函数都会被执行。
该钩子会创建一个回调函数,该函数使用 fetch API 请求给定的URL,并传入相应的选项和默认选项。
然后,它会检查响应是否正常。如果正常,则返回 JSON 格式的响应。如果异常,则返回 JSON 格式的响应并拒绝该响应。
以下是如何使用此钩子的示例:
import { useState } from "react"
import useFetch from "./useFetch"
export default function FetchComponent() {
const [id, setId] = useState(1)
const { loading, error, value } = useFetch(
`https://jsonplaceholder.typicode.com/todos/${id}`,
{},
[id]
)
return (
<div>
<div>{id}</div>
<button onClick={() => setId(currentId => currentId + 1)}>
Increment ID
</button>
<div>Loading: {loading.toString()}</div>
<div>{JSON.stringify(error, null, 2)}</div>
<div>{JSON.stringify(value, null, 2)}</div>
</div>
)
}
当您需要处理从 API 获取数据时,此钩子非常有用。它提供了一种简单的方法来管理组件中的加载、错误和值状态,并且允许组件。
11. useScript
import useAsync from "../9-useAsync/useAsync"
export default function useScript(url) {
return useAsync(() => {
const script = document.createElement("script")
script.src = url
script.async = true
return new Promise((resolve, reject) => {
script.addEventListener("load", resolve)
script.addEventListener("error", reject)
document.body.appendChild(script)
})
}, [url])
}
useScript
是一个自定义的 React hook,允许组件从给定的 URL 加载 JavaScript 文件,并跟踪加载、错误和值的状态。此外,它还使用自定义useAsync
hook,允许组件处理异步操作,并跟踪加载、错误和值的状态。
该钩子接受一个参数:
- URL是要加载的 JavaScript 文件的 URL。
该钩子会创建一个回调函数,该函数使用 DOM API 创建一个新的脚本元素,并将其 src 设置为传入的 URL。它还将async
属性设置为 true。
然后,它会返回一个新的 Promise,当脚本加载完成或出错时,该 Promise 会被 resolve 或被 rejection。
以下是如何使用此钩子的示例:
import useScript from "./useScript"
export default function ScriptComponent() {
const { loading, error } = useScript(
"https://code.jquery.com/jquery-3.6.0.min.js"
)
if (loading) return <div>Loading</div>
if (error) return <div>Error</div>
return <div>{window.$(window).width()}</div>
}
当您需要动态加载外部 JavaScript 库时,此钩子非常有用。它提供了一种简单的方法来管理组件的加载、错误和值状态,并且还允许组件在 URL 更改时重新加载脚本。
12. 使用DeepCompareEffect
import { useEffect, useRef } from "react"
import isEqual from "lodash/fp/isEqual"
export default function useDeepCompareEffect(callback, dependencies) {
const currentDependenciesRef = useRef()
if (!isEqual(currentDependenciesRef.current, dependencies)) {
currentDependenciesRef.current = dependencies
}
useEffect(callback, [currentDependenciesRef.current])
}
useDeepCompareEffect
useEffect
是一个自定义的 React hook,它允许组件仅在依赖项发生更改时才使用深度比较(而非浅层比较)来运行 effect。它使用React 库中的内置hook 和 lodash 的 isEqual 函数进行深度比较。
该钩子有两个参数:
- 回调是一个函数,代表执行后的效果。
- 依赖项是效果所依赖的值的数组。
它还会创建一个引用currentDependenciesRef
来存储当前依赖项。
然后,它会使用该函数比较当前依赖项与新的依赖项isEqual
。如果不相等,它会用新的依赖项更新当前依赖项引用。
之后,它会使用回调函数和 currentDependenciesRef.current 作为依赖项来调用 useEffect。
以下是如何使用此钩子的示例:
import { useEffect, useState, useRef } from "react"
import useDeepCompareEffect from "./useDeepCompareEffect"
export default function DeepCompareEffectComponent() {
const [age, setAge] = useState(0)
const [otherCount, setOtherCount] = useState(0)
const useEffectCountRef = useRef()
const useDeepCompareEffectCountRef = useRef()
const person = { age: age, name: "Kyle" }
useEffect(() => {
useEffectCountRef.current.textContent =
parseInt(useEffectCountRef.current.textContent) + 1
}, [person])
useDeepCompareEffect(() => {
useDeepCompareEffectCountRef.current.textContent =
parseInt(useDeepCompareEffectCountRef.current.textContent) + 1
}, [person])
return (
<div>
<div>
useEffect: <span ref={useEffectCountRef}>0</span>
</div>
<div>
useDeepCompareEffect: <span ref={useDeepCompareEffectCountRef}>0</span>
</div>
<div>Other Count: {otherCount}</div>
<div>{JSON.stringify(person)}</div>
<button onClick={() => setAge(currentAge => currentAge + 1)}>
Increment Age
</button>
<button onClick={() => setOtherCount(count => count + 1)}>
Increment Other Count
</button>
</div>
)
}
当依赖项是复杂对象或数组,并且您希望确保仅当依赖项中的特定值发生更改时才运行效果时,此钩子会非常有用。它可以帮助避免不必要的重新渲染并提高性能。
13. 使用事件监听器
import { useEffect, useRef } from "react"
export default function useEventListener(
eventType,
callback,
element = window
) {
const callbackRef = useRef(callback)
useEffect(() => {
callbackRef.current = callback
}, [callback])
useEffect(() => {
if (element == null) return
const handler = e => callbackRef.current(e)
element.addEventListener(eventType, handler)
return () => element.removeEventListener(eventType, handler)
}, [eventType, element])
}
useEventListener
useEffect
是一个自定义的 React hook,允许组件向特定的 DOM 元素添加事件监听器,并在事件发生时执行回调函数。它使用了React 库中的内置hook。
该钩子有三个参数:
- eventType是一个字符串,表示要监听的事件类型,例如“click”或“keydown”。
- 回调是一个函数,表示事件发生时要采取的操作。
- element是可选的DOM元素,用于添加事件监听器。默认值为window,表示事件监听器将被添加到全局 window 对象。
它还创建了一个 refcallbackRef
来存储当前的回调函数。useEffect
hook用于在组件挂载时设置事件监听器,并在组件卸载时移除事件监听器。当回调函数发生变化时,它还会更新回调 ref 。
以下是如何使用此钩子的示例:
import { useState } from "react"
import useEventListener from "./useEventListener"
export default function EventListenerComponent() {
const [key, setKey] = useState("")
useEventListener("keydown", e => {
setKey(e.key)
})
return <div>Last Key: {key}</div>
}
当您想要以声明的方式处理诸如点击、按键或表单提交等事件并将组件逻辑与事件处理逻辑分开时,此钩子会很有用。
14. useOnScreen
import { useEffect, useState } from "react"
export default function useOnScreen(ref, rootMargin = "0px") {
const [isVisible, setIsVisible] = useState(false)
useEffect(() => {
if (ref.current == null) return
const observer = new IntersectionObserver(
([entry]) => setIsVisible(entry.isIntersecting),
{ rootMargin }
)
observer.observe(ref.current)
return () => {
if (ref.current == null) return
observer.unobserve(ref.current)
}
}, [ref.current, rootMargin])
return isVisible
}
useOnScreen
是一个自定义的 React hook,允许组件检测特定DOMuseEffect
元素在视口内何时可见,并跟踪可见性状态。它使用了React 库中的内置hook 和IntersectionObserver API。
该钩子有两个参数:
- ref是对 DOM 元素的引用,用于检测可见性,通常使用 React“useRef”钩子创建。
- rootMargin是一个可选字符串,用于定义根元素周围的偏移量。它可用于在检查相交之前放大或缩小根元素的边界框。默认值为“0px”。
useEffect hook 用于在组件挂载时设置IntersectionObserver,并在组件卸载时移除观察者。它还会在 ref 或 rootMargin 发生变化时更新观察者。 它返回一个布尔值,指示 DOM 元素当前是否可见。isVisible
以下是如何使用此钩子的示例:
import { useRef } from "react"
import useOnScreen from "./useOnScreen"
export default function OnScreenComponentComponent() {
const headerTwoRef = useRef()
const visible = useOnScreen(headerTwoRef, "-100px")
return (
<div>
<h1>Header</h1>
<div>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Unde incidunt,
nam id itaque error dicta? Numquam earum iusto optio officia, molestias
debitis illum facilis nemo asperiores eaque voluptates modi? Dicta
mollitia fugit doloremque vitae, dolores sequi fuga quas vel incidunt
animi architecto dignissimos amet in quam praesentium corrupti voluptate
dolorem impedit numquam aut cupiditate nulla! Nisi dolore dicta, cumque
illum tempora enim dolores eum quis itaque nostrum architecto vel cum
officiis aperiam qui exercitationem voluptatibus. Veritatis unde
doloribus dolorem architecto, eum reprehenderit possimus similique eius
cum obcaecati totam placeat. Delectus nulla, quae temporibus omnis
assumenda autem ad quibusdam facilis aspernatur inventore nobis. Vitae
architecto, unde consequuntur velit consequatur dicta mollitia, fuga
iure hic accusamus blanditiis. Dignissimos, tenetur amet adipisci
nostrum perferendis ad rerum accusamus distinctio repellendus eius,
quisquam repellat nesciunt, consequatur culpa neque? Inventore vitae
laborum aperiam ullam dolorem officiis ipsum aliquid doloribus pariatur,
commodi iure illum soluta delectus, architecto ratione maiores
accusamus. Provident quia sequi dolorum asperiores necessitatibus
consequatur perspiciatis at a, inventore, deserunt corporis recusandae
earum vero voluptas saepe pariatur, libero illo. Numquam facilis magnam
exercitationem ipsam libero quidem minima dolores perferendis eveniet
impedit eos, nesciunt unde velit facere itaque eum quasi laboriosam
veritatis aliquid tenetur. Blanditiis exercitationem laborum, optio
nulla minima libero sed doloremque soluta, dignissimos tempora rerum id
nostrum iusto eveniet illo corrupti dicta. Non fuga exercitationem sit
dignissimos voluptatibus cumque nobis iste asperiores illum fugit
</div>
<h1 ref={headerTwoRef}>Header 2 {visible && "(Visible)"}</h1>
<div>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Unde incidunt,
nam id itaque error dicta? Numquam earum iusto optio officia, molestias
debitis illum facilis nemo asperiores eaque voluptates modi? Dicta
mollitia fugit doloremque vitae, dolores sequi fuga quas vel incidunt
animi architecto dignissimos amet in quam praesentium corrupti voluptate
dolorem impedit numquam aut cupiditate nulla! Nisi dolore dicta, cumque
illum tempora enim dolores eum quis itaque nostrum architecto vel cum
officiis aperiam qui exercitationem voluptatibus. Veritatis unde
doloribus dolorem architecto, eum reprehenderit possimus similique eius
cum obcaecati totam placeat. Delectus nulla, quae temporibus omnis
assumenda autem ad quibusdam facilis aspernatur inventore nobis. Vitae
architecto, unde consequuntur velit consequatur dicta mollitia, fuga
iure hic accusamus blanditiis. Dignissimos, tenetur amet adipisci
nostrum perferendis ad rerum accusamus distinctio repellendus eius,
quisquam repellat nesciunt, consequatur culpa neque? Inventore vitae
laborum aperiam ullam dolorem officiis ipsum aliquid doloribus pariatur,
commodi iure illum soluta delectus, architecto ratione maiores
accusamus. Provident quia sequi dolorum asperiores necessitatibus
consequatur perspiciatis at a, inventore, deserunt corporis recusandae
earum vero voluptas saepe pariatur, libero illo. Numquam facilis magnam
exercitationem ipsam libero quidem minima dolores perferendis eveniet
impedit eos, nesciunt unde velit facere itaque eum quasi laboriosam
veritatis aliquid tenetur. Blanditiis exercitationem laborum, optio
nulla minima libero sed doloremque soluta, dignissimos tempora rerum id
nostrum iusto eveniet illo corrupti dicta. Non fuga exercitationem sit
dignissimos voluptatibus cumque nobis iste asperiores illum fugit
veritatis fugiat quia voluptates cupiditate vel rerum eligendi facere
sint nostrum quam, maiores dolorem repellat voluptas! Magnam ullam quis
quas aut consequuntur quo doloremque, earum sint soluta vero iste quasi
voluptates labore rerum aspernatur illum esse maxime laudantium? Tempore
perspiciatis perferendis ea dolorem et quasi eos illo beatae consectetur
maxime, enim ducimus corrupti, accusantium quisquam rem dolorum itaque
iste velit. Amet similique accusamus doloribus expedita modi a
architecto accusantium labore unde non, dolore totam quaerat sit
laboriosam quae ullam impedit, pariatur repudiandae quisquam debitis
repellendus nihil. Cumque blanditiis ut recusandae illum! Maiores
eveniet nulla exercitationem natus delectus est minus a architecto
pariatur molestias quo nihil maxime quasi facere magnam neque dolorem
ad, doloribus hic! Qui corporis perspiciatis dolores rem minima tenetur.
Fugit ipsa consectetur ad reiciendis, quia iste, sapiente rerum
exercitationem reprehenderit laborum eligendi cumque? Quia porro modi
repudiandae nostrum accusamus! Corporis eum fugit nihil facilis placeat
ab est obcaecati consequuntur qui atque tempore soluta aliquid saepe
ducimus, at sed modi illo ipsa numquam ratione vero eos reprehenderit!
Sapiente nesciunt consequatur labore iste quas possimus rem cumque,
fugit laborum repellendus nisi adipisci officia temporibus quaerat!
Beatae doloribus veritatis at, maiores suscipit debitis reiciendis cum
impedit non aut modi iste? Placeat illo quisquam assumenda esse cum
ipsum quasi perspiciatis voluptatem rerum itaque, similique quidem
molestias exercitationem ullam eum amet tempore dolor aliquid unde
deserunt dolore excepturi. Aut dolore rerum sequi nihil soluta eum
expedita consequatur aliquid consequuntur saepe esse necessitatibus
repudiandae, natus, officia enim odit rem nobis adipisci, voluptates
autem dolor blanditiis ipsam animi a. Illo accusantium iure qui aperiam
commodi, quidem, dolorem error eum animi, id nam? Corporis, non
adipisci!
</div>
</div>
)
}
当您想要跟踪特定 DOM 元素何时进入视图或消失时,此钩子会很有用,例如,延迟加载图像、跟踪滚动位置或按需显示元素。
15. 使用窗口大小
import { useState } from "react"
import useEventListener from "../13-useEventListener/useEventListener"
export default function useWindowSize() {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
})
useEventListener("resize", () => {
setWindowSize({ width: window.innerWidth, height: window.innerHeight })
})
return windowSize
}
useWindowSize
是一个自定义的 React 钩子,允许组件跟踪浏览器窗口的当前大小。它使用了useState
React 库中的内置钩子和一个名为 的自定义钩子,该钩子允许组件向特定的DOMuseEventListener
元素添加事件监听器,并在事件发生时执行回调函数。
该钩子创建一个名为windowSize的对象,其中包含浏览器窗口的宽度和高度,并使用window.innerWidth
和window.innerHeight
属性设置初始状态。
它使用useEventListener钩子为 window 对象添加一个resize事件监听器,并在事件发生时使用窗口的新宽度和高度更新状态。
它返回windowSize对象,该对象包含浏览器窗口的当前宽度和高度。
以下是如何使用此钩子的示例:
import useWindowSize from "./useWindowSize"
export default function WindowSizeComponent() {
const { width, height } = useWindowSize()
return (
<div>
{width} x {height}
</div>
)
}
当您想要进行响应式设计并根据浏览器窗口的大小调整组件的布局或行为时,此钩子会很有帮助。
结论
这些自定义钩子对于构建可扩展且无 bug 的项目至关重要。即使在大型项目中,它们也能发挥神奇的作用。因此,您可以随时使用它们。感谢大家阅读这篇文章。我知道这篇文章有点长,但我希望它值得一读。下篇文章再见😊。
文章来源:https://dev.to/arafat4693/15-useful-react-custom-hooks-that-you-can-use-in-any-project-2ll8