3.5 useCookie
import { useState, useCallback } from "react"; import Cookies from "js-cookie"; type CookieHookReturn<T> = [ T | null, (newValue: T, options?: Cookies.CookieAttributes) => void, () => void ]; export default function useCookie<T>( name: string, defaultValue: T ): CookieHookReturn<T> { const [value, setValue] = useState<T | null>(() => { const cookie = Cookies.get(name); if (cookie) return JSON.parse(JSON.stringify(cookie)); Cookies.set(name, JSON.stringify(defaultValue)); return defaultValue; }); const updateCookie = useCallback( (newValue: T, options?: Cookies.CookieAttributes) => { Cookies.set(name, JSON.stringify(newValue), options); setValue(newValue); }, [name] ); const deleteCookie = useCallback(() => { Cookies.remove(name); setValue(null); }, [name]); return [value, updateCookie, deleteCookie];
在初始化时,useCookie
会检索具有指定名称的Cookie
值。
- 如果
Cookie
存在,它将返回其值; - 否则,它将
Cookie
设置为提供的默认值。
这个自定义钩子的一个主要优点是能够更新Cookie
值。由useCookie
返回的updateCookie
函数允许我们修改Cookie
的值。通过使用新值和可选的选项(如过期时间或路径)调用此函数,我们可以立即更新Cookie
。此外,该钩子方便地更新状态,使我们的应用程序与修改后的Cookie保持同步。
在需要删除Cookie
的情况下,deleteCookie
函数就派上用场了。只需调用此函数,它将从浏览器中删除指定的Cookie
。该钩子会负责更新状态,确保我们的应用程序反映了Cookie
的删除。
使用场景
useCookie
可以在各种情境中使用。在处理用户信息
、身份验证令牌
或需要跨不同会话保持的数据
时,它特别有用。
import useCookie from "@hooks/useCookie" export default function CookieComponent() { const [value, update, remove] = useCookie<string>("name", "前端柒八九"); return ( <> <div>{value}</div> <button onClick={() => update("789")}>修改cookie</button> <button onClick={remove}>移除cookie</button> </> );
3.6 useCopyToClipboard
import { useState } from "react"; import copy from "copy-to-clipboard"; interface Options { debug?: boolean; message?: string; format?: string; onCopy?: (clipboardData: object) => void; } type CopyToClipboardHookReturn = [ (text: string, options?: Options) => void, { value: string | null; success: boolean | null } ]; export default function useCopyToClipboard(): CopyToClipboardHookReturn { const [value, setValue] = useState<string | null>(null); const [success, setSuccess] = useState<boolean | null>(null); const copyToClipboard = (text: string, options?: Options) => { const result = copy(text, options); if (result) setValue(text); setSuccess(result); }; return [copyToClipboard, { value, success }]; }
在React
中文本复制是一个常见并且繁琐的事情。五星上将,麦克阿瑟
说,我们需要一个自定义hook - useCopyToClipboard
来简化这个过程。
useCopyToClipboard
钩子利用了React
的useState钩子
,以及copy-to-clipboard
库,以实现其功能。通过调用这个自定义钩子,我们可以获得两个关键功能:copyToClipboard
和相应的状态变量
。
copyToClipboard
函数接受两个参数:要复制的文本和可选的配置选项。
- 当复制成功时,提供的文本将被设置为当前值,成功状态将设置为
true
。 - 相反,如果复制失败,成功状态将保持为
false
。
使用场景
useCopyToClipboard钩子
可以在各种情境中使用。它在需要复制文本,如URL、可分享内容或用户生成的数据的情况下特别有用。
import useCopyToClipboard from "@hooks/useCopyToClipboard"; import { useRef } from "react"; export default function CopyToClipboardComponent() { const [copyToClipboard, { success, value }] = useCopyToClipboard(); const inputRef = useRef<HTMLInputElement>(null); return ( <> <button onClick={() => copyToClipboard(String(inputRef.current?.value))}> {success ? "复制过了" : "未复制"} </button> <input type="text" ref={inputRef} /> 复制的值-{value} </> ); }
3.7 useStorage
import { useCallback, useState, useEffect } from "react"; export function useLocalStorage<T>(key: string, defaultValue: T | (() => T)) { return useStorage(key, defaultValue, window.localStorage); } export function useSessionStorage<T>(key: string, defaultValue: T | (() => T)) { return useStorage(key, defaultValue, window.sessionStorage); } function useStorage<T>( key: string, defaultValue: T | (() => T), storageObject: Storage ): [T, React.Dispatch<React.SetStateAction<T>>, () => void] { const [value, setValue] = useState<T>(() => { const value = storageObject.getItem(key); if (value != null) return JSON.parse(value); if (typeof defaultValue === "function") { const value = (defaultValue as () => T)(); return value; } else { return JSON.parse(JSON.stringify(defaultValue)); } }); useEffect(() => { if (value === undefined) return storageObject.removeItem(key); storageObject.setItem(key, JSON.stringify(value)); }, [key, value, storageObject]); const remove = useCallback(() => { setValue(undefined as unknown as T); }, []); return [value, setValue, remove]; }
useStorage钩子
提供两个便捷的功能:useLocalStorage
和useSessionStorage
。
useLocalStorage
,我们可以轻松地在浏览器的本地存储中存储和检索数据,useSessionStorage
则提供了相同的功能,但是使用会话存储
。
我们可以使用它来存储任何类型的数据,如字符串、数字,甚至复杂对象。此外,useStorage
为我们处理数据的序列化和反序列化,因此我们不必担心将值转换为JSON
格式或从JSON
格式还原。
另一个优点是存储数据与组件状态之间的自动同步。每当存储的数据发生更改时,该钩子会相应地更新组件的状态。同样,当组件的状态发生更改时,该钩子会自动将新值持久化到存储中。这种双向同步确保我们的应用程序始终反映最新的数据,使其非常适合需要实时更新的场景。
useStorage钩子
还提供了一个remove
函数,允许我们在不再需要存储的值时轻松删除它们。在实现注销按钮或清除特定用户数据等功能时,此功能非常有用。
使用场景
我们可以在各种场景中使用useStorage钩子
。例如,假设我们有一个设置面板,用户可以在其中自定义其偏好设置。通过使用useLocalStorage
,我们可以轻松存储和检索这些设置,确保它们在重新加载页面时保持不变,甚至在用户关闭并重新打开浏览器时也是如此。
import { useSessionStorage, useLocalStorage } from "@hooks/useStorage"; export default function StorageComponent() { const [info, setInfo, removeInfo] = useSessionStorage<{ name: string }>( "info", { name: "front789", } ); const [age, setAge, removeAge] = useLocalStorage<number>("age", 26); return ( <div> <div> {info?.name} -{age} </div> <button onClick={() => setInfo({ name: "范美丽" })}>修改名称</button> <button onClick={() => setAge(18)}>修改年龄</button> <button onClick={removeInfo}>删除名称</button> <button onClick={removeAge}>删除年龄</button> </div> ); }
3.8 useMediaQuery
import { useState, useEffect } from "react"; import useEventListener from "@hooks/useEventListener"; export default function useMediaQuery(mediaQuery: string) : boolean { const [isMatch, setIsMatch] = useState<boolean>(false); const [mediaQueryList, setMediaQueryList] = useState<MediaQueryList | null>( null ); useEffect(() => { const list = window.matchMedia(mediaQuery); setMediaQueryList(list); setIsMatch(list.matches); }, [mediaQuery]); useEventListener( "change", (e) => setIsMatch((e as MediaQueryListEvent).matches), mediaQueryList ); return isMatch; }
useMediaQuery钩子
允许我们根据给定的媒体查询动态更新用户界面。只需将所需的媒体查询作为参数传递,该钩子将返回一个布尔值
,指示媒体查询是否与当前视口大小匹配。
使用该自定义钩子可以轻松地在整个应用程序中实现响应式行为
。无论我们需要有条件地渲染组件、应用特定的样式,还是根据屏幕大小触发不同的功能,useMediaQuery
都能满足我们的需求。
使用场景
这个钩子不仅限于特定的用例,它可以在各种场景中使用。例如,我们可以使用它动态调整导航菜单的布局,根据屏幕大小隐藏或显示某些元素,甚至可以根据可用空间优化数据的加载。useMediaQuery钩子
赋予我们在不同设备和屏幕尺寸上提供提高用户体验的能力。
import useMediaQuery from "@hooks/useMediaQuery" export default function MediaQueryComponent() { // 传人媒体查询条件 const isLarge = useMediaQuery("(min-width: 200px)") return <div>视口超过查询条件了: {isLarge.toString()}</div> }
3.9 useDarkMode
import { useEffect } from "react"; import useMediaQuery from "@hooks/useMediaQuery"; import { useLocalStorage } from "@hooks/useStorage"; type UseDarkModeReturn = [boolean, (value: boolean) => void]; export default function useDarkMode(): UseDarkModeReturn { const [darkMode, setDarkMode] = useLocalStorage("useDarkMode", false); const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)"); const enabled = darkMode ?? prefersDarkMode; useEffect(() => { document.body.classList.toggle("dark-mode", enabled); }, [enabled]); const toggleDarkMode = (value: boolean) => { setDarkMode(value); }; return [enabled, toggleDarkMode]; }
这个自定义钩子结合了另外两个方便的钩子useMediaQuery
和useStorage
,以提供一个快速切换应用深色模式的功能。它自动检测用户的首选颜色方案,并将深色模式状态保留在浏览器的本地存储中。
useDarkMode钩子
在启用深色模式时动态更新HTML body的类,以应用dark-mode
样式。这种方法确保了在所有组件中的一致性,而无需手动进行类的操作。
body.css
body.dark-mode { background-color: #333; }
我们可以在各种情境中使用useDarkMode钩子
。无论我们是无论构建博客、还是电商平台,深色模式
都可以提升用户体验,减轻眼部压力,并延长设备电池寿命。
使用场景
通过点击切换 Dark Mode
按钮,我们可以立即在浅色和深色主题之间切换。按钮的外观会动态改变,反映当前的模式。
import useDarkMode from "@hooks/useDarkMode"; import "./body.css"; export default function DarkModeComponent() { const [darkMode, setDarkMode] = useDarkMode(); return ( <button onClick={() => setDarkMode(!darkMode)} style={{ border: `1px solid ${darkMode ? "white" : "black"}`, background: "none", color: darkMode ? "white" : "black", }} > 切换 Dark Mode </button> ); }
3.10 useTimeout
import { useCallback, useEffect, useRef } from "react"; type TimeoutHookReturn = { reset: () => void; clear: () => void; }; export default function useTimeout(callback: () => void, delay: number): TimeoutHookReturn { const callbackRef = useRef<() => void>(callback); const timeoutRef = useRef<number | undefined>(); 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 组件中设置、清除和重置超时的逻辑。它接受两个参数:回调函数和延迟持续时间(以毫秒为单位)。每当指定的延迟时间过去时,将执行提供的回调函数。
这个自定义钩子的一个重要优点是,它确保即使在组件重新渲染期间更改,回调函数仍然保持最新状态。通过使用 useRef
来存储回调引用,该钩子保证始终调用最新版本的函数。
此外,useTimeout钩子
通过使用 useCallback
来记忆 set
和 clear
函数,优化了性能。这意味着只有在它们的依赖项更改时才重新创建这些函数,从而防止不必要的渲染,提高了效率。
使用场景
useTimeout 钩子
可以在需要定时操作的各种场景中使用。例如,在倒计时组件中,以轻松地实现在特定持续时间后重置的计时器。
import { useState } from "react"; import useTimeout from "@hooks/useTimeout"; export default function TimeoutComponent() { const [count, setCount] = useState(10); const { clear, reset } = useTimeout(() => setCount(789), 1000); return ( <div> <div>{count}</div> <button onClick={() => setCount((c) => c + 1)}>数据+1</button> <button onClick={clear}>清除定时器</button> <button onClick={reset}>设定回调函数,将数字设置为789</button> </div> ); }
3.11 useDebounce
import { useEffect, DependencyList } from "react"; import useTimeout from "@hooks/useTimeout"; export default function useDebounce( callback: () => void, delay: number, dependencies: DependencyList ) { const { reset, clear } = useTimeout(callback, delay); useEffect(reset, [...dependencies, reset]); useEffect(clear, [clear]); }
useDebounce钩子
内部利用useTimeout钩子
来延迟执行回调函数,直到指定的延迟时间已过。通过这样做,它防止了由于快速输入更改或重复事件引起的频繁更新,从而实现更流畅的交互和减少资源消耗。
useDebounce
通过将回调函数
、延迟持续时间
以及任何依赖项包装在这个自定义钩子中,我们可以轻松实现防抖功能,而无需使组件代码混乱不堪。该钩子负责管理超时并在必要时清除它,确保仅在指定的延迟时间和最新的依赖项后触发回调。
使用场景
这个自定义钩子在需要处理用户输入的情况下特别有用,比如搜索栏或表单字段,我们希望延迟执行某个操作,直到用户完成输入或交互。它还可用于优化网络请求,确保仅在用户停止输入或选择选项后发送请求。
import { useState } from "react"; import useDebounce from "@hooks/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)}>数字+1</button> </div> ); }
每当用户点击数字+1
按钮时,计数状态会更新。但是,我们不会立即弹出计数值,而是使用useDebounce
来防抖回调函数。只有在延迟1秒后,计数值才会弹出,有效地防止了在快速点击按钮时弹出过多的输出。
3.12 useToggle
import { useState } from "react"; export default function useToggle(defaultValue: boolean) { const [value, setValue] = useState(defaultValue); function toggleValue(value: boolean | undefined) { setValue((currentValue) => typeof value === "boolean" ? value : !currentValue ); } return [value, toggleValue] as const; }
useToggle
只需一行代码,我们就可以使用默认值初始化状态。toggleValue
函数使我们能够轻松地在 true
和 false
之间切换状态,或者我们可以直接传递一个布尔值来将状态设置为所需的值。这种多功能性使 useToggle
成为各种需要切换或改变状态的场景的理想选择。
使用场景
使用 useToggle
钩子来管理切换按钮的状态。通过简单的单击,按钮的状态在 true
和 false
之间切换。此外,该钩子提供了按钮,允许直接将值设置为 true
或 false
,以满足特定用例。
import useToggle from "@hooks/useToggle"; export default function ToggleComponent() { const [value, toggleValue] = useToggle(false); return ( <div> <div>{value.toString()}</div> <button onClick={() => toggleValue(!value)}>状态切换</button> <button onClick={() => toggleValue(true)}>直接设置为true</button> <button onClick={() => toggleValue(false)}>直接设置为false</button> </div> ); }