15 个实用的 React 自定义 Hook,可用于任何项目

2025-05-27

15 个实用的 React 自定义 Hook,可用于任何项目

React 自定义 hooks 允许在函数式组件中复用逻辑,从而可以分离组件,保持各个部分精简,并专注于其预期用途。自定义 hooks 还可以更轻松地测试和理解组件的逻辑,因为它可以与组件本身隔离并单独测试。自定义 hooks 还可以简化不同组件之间的逻辑共享,减少代码重复,并更轻松地维护和更新代码库。

React Hooks 的可能性无限,但并非所有 Hooks 都生而平等。在本文中,我将介绍 30 个强大的自定义 Hooks,它们可以在任何项目中使用,并且实现起来也非常快捷。

注意:这些钩子是由 Youtuber Web Dev Simplified创建的。如果您需要更深入的视频讲解,可以观看他的视频。但是,如果您只想阅读一下,请继续阅读。

目录


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]
}
Enter fullscreen mode Exit fullscreen mode

useToggle是一个自定义的 React Hook,允许组件在 true 和 false 之间切换值。它使用useStateHook 来管理组件的状态。首先,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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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 }
}
Enter fullscreen mode Exit fullscreen mode

useTimeout是一个自定义的 React 钩子,允许组件执行setclear设置超时。它使用了ReactuseCallback库中的、useEffectuseRef钩子。该钩子接受两个参数:一个将在指定延迟时间后调用的回调函数,以及一个延迟时间(以毫秒为单位),该延迟时间表示在调用回调函数之前应经过的时间。

该钩子返回一个具有两个属性的对象:resetclear,可用于重置清除超时的函数。

该钩子使用useRef钩子创建两个引用:callbackReftimeoutRef。将回调callbackRef函数保存为可变值,并包含setTimeout()函数返回的超时idtimeoutRef

useEffect hook 用于确保回调函数 callbackRef.current始终传递最新的回调。
该函数使用setTimeoutset创建一个新的超时时间,并在指定的延迟时间后调用回调函数。clear函数使用clearTimeout清除超时时间。此外,还有一个hook 用于在挂载时设置超时时间,并在卸载时移除超时时间。reset 函数是函数的组合。最后,该hook 确保仅当依赖关系发生变化时才会重新创建函数。useEffectclearsetuseCallback

以下是如何使用此钩子的示例:

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

这个自定义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, [])
}
Enter fullscreen mode Exit fullscreen mode

useDebounceuseEffect是一个自定义的 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>
  )
}
Enter fullscreen mode Exit fullscreen mode

当您想要限制短时间内回调函数的调用次数时,此钩子会非常有用。例如,当您有一个输入字段,每次用户击键都会向服务器发送搜索请求时,您应该等待用户停止输入后再发送请求,以避免不必要的网络流量并提升用户体验。

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)
}
Enter fullscreen mode Exit fullscreen mode

useUpdateEffect是一个自定义的 React hook,允许组件仅在特定依赖项发生变化时运行回调函数。它使用了 React 库的内置useEffecthooks useRef

该钩子接受两个参数:

  • callback是依赖项发生改变时应该调用的函数
  • dependencies是钩子应该监听变化的值数组。

该钩子使用useRef钩子创建一个firstRenderRef初始值为true的引用。此引用将用于跟踪组件的首次渲染。useEffect
钩子用于监听依赖项数组的变化并调用回调函数。在函数内部,它通过检查值来判断这是否是组件的首次渲染。如果是,则将其设置为 false 并返回。如果不是,则表示这是一次更新,因此它将调用回调函数并返回回调函数。useEffectfirstRenderRef

以下是如何使用此钩子的示例:

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

如果您希望仅在特定值更改时运行某些逻辑,而不是在初始渲染时运行,那么此钩子会非常有用。例如,当您想在用户从下拉菜单中选择特定选项后从 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 }
}
Enter fullscreen mode Exit fullscreen mode

useArray是一个自定义的 React hook,允许组件管理数组状态。它使用 React 库中的内置useStatehook。该 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>
  )
}
Enter fullscreen mode Exit fullscreen mode

当您想要管理组件状态中的数据数组并执行日常数组操作(例如添加、删除、更新和过滤元素)时,此钩子会很有用。

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
}
Enter fullscreen mode Exit fullscreen mode

usePrevious是一个自定义的 React hook,允许组件跟踪变量的先前值。它使用了React库中的内置useRefhook

该钩子接受一个参数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>
  )
}
Enter fullscreen mode Exit fullscreen mode

当您需要访问变量的先前值时,此钩子会很有用,例如,当您想将当前值与先前的值进行比较以检查它是否已更改,或者当您想要跟踪变量随时间的变化时。

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,
    },
  ]
}
Enter fullscreen mode Exit fullscreen mode

useStateWithHistory是一个自定义的 React hook,允许组件跟踪状态的历史记录。它使用了React 库中内置的useStateuseCallback和钩子。useRef

该钩子接受两个参数:

  • defaultValue是状态的初始值
  • capacity是一个可选参数,用于设置历史记录中应存储的最大状态数。

该钩子创建了两个引用,一个名为 ,historyRef用于保存状态历史记录的数组;另一个名为pointerRef,用于保存历史记录的当前指针。它还创建了三个回调函数:setbackforward
set函数用于设置状态,其工作原理与内置setState函数类似,但它还通过向历史记录数组添加新值并更新 pointerRef 来跟踪状态的历史记录。该函数可以接受一个值或一个接收当前状态作为参数的回调函数。该函数还会通过删除最旧的元素来确保历史记录数组的容量不会超出。

back函数导航到历史记录中的先前状态。它减少pointerRef 的值,并使用历史记录数组中先前的值更新状态。

forward函数导航到历史记录中的下一个状态。它增加 pointerRef 的值,并使用历史记录数组中的下一个值更新状态。

go函数导航到历史记录中的特定状态。它将 pointerRef 设置为作为参数传递的索引,并使用历史记录数组中该索引处的值更新状态。

该钩子返回一个包含两个元素的数组:

  • 当前状态值
  • set包含历史数组、指针以及函数、backforward和 的对象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>
  )
}
Enter fullscreen mode Exit fullscreen mode

当您想要跟踪状态历史时,此钩子会很有用,例如,当您想要实现撤消或重做功能或允许用户浏览更改历史时。

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]
}
Enter fullscreen mode Exit fullscreen mode

useLocalStorageand是一个自定义useSessionStorageReact hook,允许组件将值存储在浏览器的LocalStorageSessionStorage中,并使其与组件的状态保持同步。它使用了React 库中的内置hooks 和hooks。useStateuseEffectuseCallback

useLocalStorage函数useSessionStorage类似,但分别使用不同的存储方式: localStoragesessionStorage。它们接受两个参数:keydefaultValuekey是用于将值存储在存储对象中的键,defaultValue是当存储对象中找不到该键时将使用的值。

两个函数都使用该storage函数,该函数接受三个参数:key、、defaultValuestorageObject,并返回一个包含三个元素的数组:

  • 当前值
  • 函数“setValue”可用于更新状态和存储中的值。
  • 函数“remove”可用于从状态和存储中删除值。

useEffect钩子使存储在浏览器存储中的值与组件的状态保持同步。函数使用JSON.stringify()JSON.parse方法,在将值存储到存储对象时将其转换为JSON字符串并在从存储对象中检索时将其转换回 JavaScript 对象。这使得该钩子可以处理任何数据,而不仅仅是字符串。
useStorage

每当keyvaluestorageObject发生变化时,钩子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>
  )
}
Enter fullscreen mode Exit fullscreen mode

如果您希望跨浏览器会话或页面持久化数据,并使数据与组件状态保持同步,此钩子会非常有用。例如,您可以存储用户的设置、表单的数据或待办事项列表。使用 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 }
}
Enter fullscreen mode Exit fullscreen mode

useAsync是一个自定义的 React 钩子,允许组件处理异步操作并跟踪加载错误状态。它使用了React 库中的内置useState钩子和钩子。useEffectuseCallback

该钩子接受两个参数:

  • callback是一个返回 Promise 的函数。该函数负责执行异步操作。
  • dependencies是钩子需要监听的依赖项变化的数组。当任何依赖项发生变化时,回调函数都会被执行。

该钩子创建了三个状态变量:loadingerrorvalue。loading状态用于指示异步操作当前是否正在进行中;error状态用于在 Promise 被拒绝的情况下存储错误对象;value状态用于在 Promise 被满足的情况下存储已解析的值。

该钩子还创建了一个名为callbackMemoizedusing的回调函数useCallback。此函数将loadingerrorvalue状态设置为其初始值,然后调用传入的回调函数。当依赖项发生变化时,
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>
  )
}
Enter fullscreen mode Exit fullscreen mode

当您需要处理异步操作(例如从 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)
}
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

当您需要处理从 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])
}
Enter fullscreen mode Exit fullscreen mode

useScript是一个自定义的 React hook,允许组件从给定的 URL 加载 JavaScript 文件,并跟踪加载、错误和值的状态。此外,它还使用自定义useAsynchook,允许组件处理异步操作,并跟踪加载错误值的状态。

该钩子接受一个参数:

  • 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>
}
Enter fullscreen mode Exit fullscreen mode

当您需要动态加载外部 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])
}
Enter fullscreen mode Exit fullscreen mode

useDeepCompareEffectuseEffect是一个自定义的 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>
  )
}
Enter fullscreen mode Exit fullscreen mode

当依赖项是复杂对象或数组,并且您希望确保仅当依赖项中的特定值发生更改时才运行效果时,此钩子会非常有用。它可以帮助避免不必要的重新渲染并提高性能。

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])
}
Enter fullscreen mode Exit fullscreen mode

useEventListeneruseEffect是一个自定义的 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>
}
Enter fullscreen mode Exit fullscreen mode

当您想要以声明的方式处理诸如点击、按键或表单提交等事件并将组件逻辑与事件处理逻辑分开时,此钩子会很有用。

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
}
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

当您想要跟踪特定 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
}
Enter fullscreen mode Exit fullscreen mode

useWindowSize是一个自定义的 React 钩子,允许组件跟踪浏览器窗口的当前大小。它使用了useStateReact 库中的内置钩子和一个名为 的自定义钩子,该钩子允许组件向特定的DOMuseEventListener元素添加事件监听器,并在事件发生时执行回调函数。

该钩子创建一个名为windowSize的对象,其中包含浏览器窗口的宽度和高度,并使用window.innerWidthwindow.innerHeight属性设置初始状态。

它使用useEventListener钩子为 window 对象添加一个resize事件监听器,并在事件发生时使用窗口的新宽度和高度更新状态。
它返回windowSize对象,该对象包含浏览器窗口的当前宽度和高度。

以下是如何使用此钩子的示例:

import useWindowSize from "./useWindowSize"

export default function WindowSizeComponent() {
  const { width, height } = useWindowSize()

  return (
    <div>
      {width} x {height}
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

当您想要进行响应式设计并根据浏览器窗口的大小调整组件的布局或行为时,此钩子会很有帮助。


结论

这些自定义钩子对于构建可扩展且无 bug 的项目至关重要。即使在大型项目中,它们也能发挥神奇的作用。因此,您可以随时使用它们。感谢大家阅读这篇文章。我知道这篇文章有点长,但我希望它值得一读。下篇文章再见😊。

文章来源:https://dev.to/arafat4693/15-useful-react-custom-hooks-that-you-can-use-in-any-project-2ll8
PREV
8 款适用于 Web 开发者的出色 Chrome 扩展程序
NEXT
Web 应用程序安全检查表 (2021)