发布于 2026-01-05 6 阅读
0

作为前端开发者,我总是会用到的 7 个 React 自定义 Hooks 🚀

作为前端开发者,我总是会用到的 7 个 React 自定义 Hooks 🚀

自定义 Hooks 不仅仅是 React 中的一项便利功能,它们彻底改变了代码的模块化和可维护性。它们允许开发者封装逻辑、管理状态,并以以前无法实现的方式简化复杂的功能。

图片描述

React 强大的函数式编程范式重新定义了现代前端开发,为模块化、可维护和可复用的代码铺平了道路。在其众多功能中,自定义 Hooks 脱颖而出,成为构建更智能、更简洁组件的关键工具。今天,我们将深入探讨一些每位开发者都应该掌握的基本自定义 Hooks,并学习如何有效地实现它们。


  1. useFetch:简化 API 调用 🌐 获取数据是 React 中的常见任务。useFetch hook 抽象了重复的逻辑,简化了 API 调用并优雅地管理状态。

执行:

import { useState, useEffect } from "react";

function useFetch<T>(url: string) {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      try {
        const response = await fetch(url);
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err as Error);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
}

export default useFetch;
Enter fullscreen mode Exit fullscreen mode

用法:

const { data, loading, error } = useFetch<User[]>('/api/users');

if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;

return <ul>{data?.map(user => <li key={user.id}>{user.name}</li>)}</ul>;
Enter fullscreen mode Exit fullscreen mode

  1. useDebounce:优化性能⏳ 通过防抖钩子可以高效地处理频繁的用户输入,例如搜索或表单字段,从而减少不必要的渲染和 API 调用。

执行:

import { useState, useEffect } from "react";

function useDebounce<T>(value: T, delay: number): T {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => setDebouncedValue(value), delay);

    return () => clearTimeout(handler);
  }, [value, delay]);

  return debouncedValue;
}

export default useDebounce;
Enter fullscreen mode Exit fullscreen mode

用法:

const [searchTerm, setSearchTerm] = useState('');
const debouncedSearch = useDebounce(searchTerm, 500);

useEffect(() => {
  if (debouncedSearch) {
    // Trigger API or other actions
  }
}, [debouncedSearch]);
Enter fullscreen mode Exit fullscreen mode

  1. useToggle:轻松管理布尔状态“使用自定义 useToggle 钩子可以轻松管理模态框、下拉菜单或主题切换的切换状态,从而保持代码的简洁性和可重用性。

执行

import { useState } from "react";

function useToggle(initialState = false) {
  const [state, setState] = useState(initialState);

  const toggle = () => setState(prev => !prev);

  return [state, toggle] as const;
}

export default useToggle;
Enter fullscreen mode Exit fullscreen mode

用法:

const [isModalOpen, toggleModal] = useToggle();

return (
  <div>
    <button onClick={toggleModal}>Toggle Modal</button>
    {isModalOpen && <p>Modal Content</p>}
  </div>
);
Enter fullscreen mode Exit fullscreen mode

  1. useLocalStorage:本地持久化数据📂 通过自定义 useLocalStorage 钩子,从 localStorage 存储和检索数据变得无缝且可重用。

执行:

import { useState } from "react";

function useLocalStorage<T>(key: string, initialValue: T) {
  const [storedValue, setStoredValue] = useState<T>(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(error);
      return initialValue;
    }
  });

  const setValue = (value: T) => {
    try {
      setStoredValue(value);
      window.localStorage.setItem(key, JSON.stringify(value));
    } catch (error) {
      console.error(error);
    }
  };

  return [storedValue, setValue] as const;
}

export default useLocalStorage;
Enter fullscreen mode Exit fullscreen mode

用法:

const [theme, setTheme] = useLocalStorage('theme', 'light');

return (
  <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
    Toggle Theme
  </button>
);
Enter fullscreen mode Exit fullscreen mode

  1. usePrevious:跟踪先前的状态📜 跟踪值的先前状态对于比较和动画至关重要,使用自定义 usePrevious hook 可以轻松实现。

执行:

import { useEffect, useRef } from "react";

function usePrevious<T>(value: T): T | undefined {
  const ref = useRef<T>();

  useEffect(() => {
    ref.current = value;
  }, [value]);

  return ref.current;
}

export default usePrevious;
Enter fullscreen mode Exit fullscreen mode

用法:

const [count, setCount] = useState(0);
const prevCount = usePrevious(count);

return (
  <p>
    Now: {count}, Before: {prevCount}
  </p>
);
Enter fullscreen mode Exit fullscreen mode

  1. useClickOutside:检测外部点击🖱️ 非常适合在点击外部时关闭模态框或下拉菜单,使用自定义 useClickOutside 钩子以获得更好的用户体验。

执行:

import { useEffect, useRef } from "react";

function useClickOutside(handler: () => void) {
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (ref.current && !ref.current.contains(event.target as Node)) {
        handler();
      }
    };

    document.addEventListener('mousedown', handleClickOutside);
    return () => document.removeEventListener('mousedown', handleClickOutside);
  }, [handler]);

  return ref;
}

export default useClickOutside;
Enter fullscreen mode Exit fullscreen mode

用法:

const ref = useClickOutside(() => setDropdownOpen(false));

return (
  <div ref={ref}>
    {dropdownOpen && <p>Dropdown Content</p>}
  </div>
);
Enter fullscreen mode Exit fullscreen mode

  1. useMediaQuery:处理响应式设计📱 使用自定义 useMediaQuery hook 可以简化 React 中的媒体查询管理,使响应式设计更加高效。

执行:

import { useState, useEffect } from "react";

function useMediaQuery(query: string): boolean {
  const [matches, setMatches] = useState(false);

  useEffect(() => {
    const mediaQueryList = window.matchMedia(query);

    const updateMatch = () => setMatches(mediaQueryList.matches);
    updateMatch();

    mediaQueryList.addEventListener('change', updateMatch);
    return () => mediaQueryList.removeEventListener('change', updateMatch);
  }, [query]);

  return matches;
}

export default useMediaQuery;
Enter fullscreen mode Exit fullscreen mode

用法:

const isMobile = useMediaQuery('(max-width: 768px)');

return <p>{isMobile ? 'Mobile View' : 'Desktop View'}</p>;
Enter fullscreen mode Exit fullscreen mode

自定义 Hooks 展现了 React 的灵活性和强大功能,能够编写更简洁、更易复用、更易于维护的代码

通过利用自定义钩子,开发者可以简化复杂的功能,并创建可重用的高效代码。以上示例展示了这些钩子如何优雅地解决常见难题。


希望这对您有所帮助!如果您能在领英上与我联系,我会非常高兴🚀

文章来源:https://dev.to/joodi/7-react-custom-hooks-i-always-use-as-a-front-end-developer-5i9