美丽的公主和它的27个React 自定义 Hook(二)

简介: 美丽的公主和它的27个React 自定义 Hook(二)

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钩子利用了ReactuseState钩子,以及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钩子提供两个便捷的功能:useLocalStorageuseSessionStorage

  • 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];
}

这个自定义钩子结合了另外两个方便的钩子useMediaQueryuseStorage,以提供一个快速切换应用深色模式的功能。它自动检测用户的首选颜色方案,并将深色模式状态保留在浏览器的本地存储中。

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 来记忆 setclear 函数,优化了性能。这意味着只有在它们的依赖项更改时才重新创建这些函数,从而防止不必要的渲染,提高了效率。

使用场景

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 函数使我们能够轻松地在 truefalse 之间切换状态,或者我们可以直接传递一个布尔值来将状态设置为所需的值。这种多功能性使 useToggle 成为各种需要切换或改变状态的场景的理想选择。

使用场景

使用 useToggle 钩子来管理切换按钮的状态。通过简单的单击,按钮的状态在 truefalse 之间切换。此外,该钩子提供了按钮,允许直接将值设置为 truefalse,以满足特定用例。

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>
  );
}
相关文章
|
15天前
|
前端开发 JavaScript 定位技术
Docusaurus框架——react+antd+echarts自定义mdx生成图表代码解释文档
Docusaurus框架——react+antd+echarts自定义mdx生成图表代码解释文档
28 0
|
16天前
|
前端开发 JavaScript CDN
前端react 18.2整合ckeditor富文本编辑器——配置插件、自定义toolbar工具栏(一)
前端react 18.2整合ckeditor富文本编辑器——配置插件、自定义toolbar工具栏
30 0
|
29天前
|
前端开发 JavaScript
【边做边学】React Hooks (二)——useEffect Hook
【边做边学】React Hooks (二)——useEffect Hook
|
1月前
|
前端开发 JavaScript
React中useEffect Hook使用纠错
React中useEffect Hook使用纠错
17 0
|
5月前
|
自然语言处理 前端开发 JavaScript
说说你对 React Hook的闭包陷阱的理解,有哪些解决方案?
说说你对 React Hook的闭包陷阱的理解,有哪些解决方案?
52 0
|
5月前
|
自然语言处理 前端开发 JavaScript
美丽的公主和它的27个React 自定义 Hook(四)
美丽的公主和它的27个React 自定义 Hook(四)
|
5月前
|
存储 前端开发 数据可视化
美丽的公主和它的27个React 自定义 Hook(三)
美丽的公主和它的27个React 自定义 Hook(三)
|
18天前
|
前端开发 测试技术 开发工具
探索前端框架React Hooks的优势与应用
本文将深入探讨前端框架React Hooks的优势与应用。通过分析React Hooks的特性以及实际应用案例,帮助读者更好地理解和运用这一现代化的前端开发工具。
|
11天前
|
开发框架 Dart 前端开发
【Flutter前端技术开发专栏】Flutter与React Native的对比与选择
【4月更文挑战第30天】对比 Flutter(Dart,强类型,Google支持,快速热重载,高性能渲染)与 React Native(JavaScript,庞大生态,热重载,依赖原生渲染),文章讨论了开发语言、生态系统、性能、开发体验、学习曲线、社区支持及项目选择因素。两者各有优势,选择取决于项目需求、团队技能和长期维护考虑。参考文献包括官方文档和性能比较文章。
【Flutter前端技术开发专栏】Flutter与React Native的对比与选择
|
11天前
|
前端开发 JavaScript 开发者
【专栏:HTML与CSS前端技术趋势篇】前端框架(React/Vue/Angular)与HTML/CSS的结合使用
【4月更文挑战第30天】前端框架React、Vue和Angular助力UI开发,通过组件化、状态管理和虚拟DOM提升效率。这些框架与HTML/CSS结合,使用模板语法、样式管理及组件化思想。未来趋势包括框架简化、Web组件标准采用和CSS在框架中角色的演变。开发者需紧跟技术发展,掌握新工具,提升开发效能。