自定义 Hooks 实战(下)

引言

在 React 开发中,自定义 Hooks 是提取组件逻辑、实现代码复用的强大工具。上篇文章我们介绍了自定义 Hooks 的基本概念,今天我们将深入三个实用的自定义 Hooks:useFetchuseDebounceuseInterval,看看它们如何解决实际开发中的常见问题。


1. useFetch - 优雅的数据获取

使用场景

处理 API 请求时,我们需要管理加载状态、错误处理和响应数据。useFetch 将这些逻辑封装成一个可复用的 Hook。

完整实现

typescript 复制代码
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 () => {
      try {
        const response = await fetch(url);
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err as Error);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
}

使用示例

javascript 复制代码
function UserProfile({ userId }: { userId: string }) {
  const { data, loading, error } = useFetch<User>(
    `/api/users/${userId}`
  );

  if (loading) return <div>加载中...</div>;
  if (error) return <div>错误:{error.message}</div>;
  if (!data) return null;

  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.email}</p>
    </div>
  );
}

2. useDebounce - 防抖优化

使用场景

在搜索框输入、窗口大小调整等高频触发场景中,useDebounce 可以延迟执行,避免不必要的计算和请求。

完整实现

typescript 复制代码
import { useState, useEffect } from 'react';

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

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

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

  return debouncedValue;
}

使用示例

ini 复制代码
function SearchBox() {
  const [searchTerm, setSearchTerm] = useState('');
  const debouncedSearchTerm = useDebounce(searchTerm, 500);

  useEffect(() => {
    // 只在用户停止输入 500ms 后执行搜索
    if (debouncedSearchTerm) {
      fetchSearchResults(debouncedSearchTerm);
    }
  }, [debouncedSearchTerm]);

  return (
    <input
      type="text"
      value={searchTerm}
      onChange={(e) => setSearchTerm(e.target.value)}
      placeholder="搜索..."
    />
  );
}

3. useInterval - 定时器 Hook

使用场景

需要周期性执行某些操作(如轮询、动画、倒计时)时,useInterval 提供了声明式的定时器管理。

完整实现

javascript 复制代码
import { useEffect, useRef } from 'react';

function useInterval(callback: () => void, delay: number | null) {
  const savedCallback = useRef<() => void>();

  // 记住最新的回调函数
  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  // 设置定时器
  useEffect(() => {
    function tick() {
      savedCallback.current?.();
    }

    if (delay !== null) {
      const id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}

使用示例

scss 复制代码
function Counter() {
  const [count, setCount] = useState(0);

  useInterval(() => {
    setCount(count + 1);
  }, 1000);

  return <div>计数:{count}</div>;
}

总结对比

Hook 核心用途 关键特性 典型场景
useFetch 数据获取 自动管理 loading/error/state API 调用、数据加载
useDebounce 防抖优化 延迟值更新、自动清理定时器 搜索框、输入验证
useInterval 定时执行 支持动态延迟、自动清理 轮询、动画、倒计时

最佳实践建议

  1. 命名规范 :自定义 Hook 必须以 use 开头
  2. 依赖管理 :确保 useEffect 依赖数组完整
  3. 清理副作用:返回清理函数避免内存泄漏
  4. 类型安全:使用 TypeScript 泛型提高类型推断
  5. 测试覆盖:为自定义 Hook 编写单元测试
相关推荐
binbin_5218 小时前
React 井字棋教程:用一个小游戏理解组件和状态
前端·react.js·前端框架
张元清19 小时前
React useObjectUrl Hook:预览文件与 Blob,不留内存泄漏(2026)
javascript·react.js
真的想不出名儿1 天前
Md编辑器整合-react
前端·react.js·编辑器
牛仔只喝牛奶2 天前
多端跨端技术选型:小程序 + H5 + App 的两栈拆分落地
react native·react.js
wear工程师2 天前
setState 到底是不是异步的?这题从 React 16 问到 18,标准答案早就变了
react.js·面试
wanghowie2 天前
LangGraph4j 落地(二)— ReAct 多步工具与 trace 扩展
前端·react.js·前端框架
csj502 天前
前端基础之《React(11)—state声明式》
前端·react.js
张元清2 天前
React useLocalStorage Hook:SSR 安全的持久化状态(2026)
javascript·react.js
析数塔2 天前
2026前端框架深度解析:React Compiler、Angular Zoneless、Vue 3.5重写游戏规则
前端·vue.js·react.js
杖雍皓2 天前
没有服务器如何构建现代 Web 应用:平台选型与组合策略
运维·服务器·前端·react.js·github·netlify·vercel