自定义 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 编写单元测试
相关推荐
Revolution612 小时前
React 组件重新渲染时,到底重新执行了什么
前端·react.js·面试
林焱_RPAAI2 小时前
影刀RPA技术深度:CSS选择器高级实战指南——伪类属性选择器与性能对比完全解析
vue.js·react.js
名字还没想好☜9 小时前
React 受控输入框光标跳到末尾:格式化输入时的 selection 丢失 bug 与修复
前端·javascript·react.js·bug·react·next.js
10share9 小时前
React 新一代样式隔离方案 —— 编译时、零运行时、原生写法
前端·react.js
光影少年10 小时前
RN 的EventEmitter 双向通信
前端·react native·react.js
GuWenyue1 天前
90%前端写React+TS都踩坑!从组件类型、单向数据流到本地存储完整实战
前端·react.js
张元清1 天前
React useThrottle Hook:节流值与回调(2026)
javascript·react.js
烬羽1 天前
React 状态提升:一个 ColorPicker,彻底搞懂状态该放哪里
react.js·typescript·设计
console.log('npc')1 天前
React 跨项目集成实战:iframe 实现子项目详情弹窗
前端·javascript·react.js
无人生还1 天前
从 Vue3 到 React · 快速上手系列第 8 篇:组件通信与 Context(对标插槽与 provide/inject)
前端·vue.js·react.js