自定义hook

119 阅读4分钟

创建自定义 Hook:

  1. 以 "use" 开头的函数名:

    • 自定义 Hook 的函数名称应该以 "use" 开头,这是 React 的约定,以便开发者识别它们是 Hook。
  2. 提取状态逻辑:

    • 将要共享的状态逻辑从组件中提取到自定义 Hook 中。

示例:GET请求hook

import { useState, useEffect } from 'react';

function useHttpGet(url) {
  // 定义状态
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    // 发起 GET 请求
    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 (error) {
        setError(error);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]); // 仅在 url 发生变化时触发 useEffect

  // 返回状态和重新发起请求的函数
  return { data, loading, error, refetch: () => fetchData() };
}

// 使用自定义 Hook 的组件
function MyComponent() {
  const { data, loading, error, refetch } = useHttpGet('<https://api.example.com/data>');

  if (loading) {
    return <p>Loading...</p>;
  }

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

  return (
    <div>
      {/* 使用获取到的数据 */}
      <p>Data: {JSON.stringify(data)}</p>
      {/* 提供重新发起请求的按钮 */}
      <button onClick={refetch}>Refetch Data</button>
    </div>
  );
}

示例:防抖hook

function useDebounce(fn, delay, dep = []) {
  // 使用 useRef 创建 current 对象 ,用于存储 当前目标函数 和 定时器
  const { current } = useRef({ fn, timer: null });

  // 使用 useEffect 监听 fn 参数的变化,更新 当前目标函数
  useEffect(function () {
    current.fn = fn;
  }, [fn]);

  // 返回一个使用 useCallback 创建的防抖函数 f
  return useCallback(function f(...args) {
    // 如果存在已计划的定时器,取消它
    if (current.timer) {
      clearTimeout(current.timer);
    }

    // 设置一个新的定时器,延迟 delay 毫秒后执行 当前目标函数
    current.timer = setTimeout(() => {
      current.fn.call(this, ...args);
    }, delay);
  // 通过 dep 作为依赖项传递给 useCallback,以确保在依赖项变化时重新创建防抖函数
  }, dep)
}
💡 思路:
  1. useDebounce 接受三个参数:

    • fn: 要防抖的函数,即希望延迟执行的函数。
    • delay: 防抖的延迟时间,单位是毫秒。
    • dep(可选): 依赖数组,用于在 useCallback 内部的 useEffect 钩子中。它允许你控制防抖函数的依赖项,以便在特定依赖项变化时重置防抖。
  2. 使用 useRef 存储当前目标函数 fn 和定时器 timer 【用于延迟执行】

  3. 使用 useEffect 监听 fn 参数的变化。如果 fn 发生变化,将更新当前目标函数。

  4. 返回一个使用 useCallback 创建的函数 f,它是防抖函数。

    1. f 函数内部,首先检查是否存在已经计划的定时器(current.timer),如果有的话,使用 clearTimeout 取消之前的定时器。

    2. 然后,设置一个新的定时器,等待指定的 delay 时间后执行当前目标函数 current.fn。这确保了只有最后一次触发 f 函数会实际执行 fn 函数。

    3. 最后,将 dep 作为依赖项传递给 useCallback,以确保在依赖项变化时重新创建 f 函数。这是为了处理特殊情况,如果你想在某些依赖项变化时重置防抖函数的状态,可以在 dep 数组中列出这些依赖项。

示例:节流hook

function useThrottle(fn, delay, dep = []) {
  // 使用 useRef 创建 current 对象 ,用于存储 当前目标函数 和 定时器
  const { current } = useRef({ fn, timer: null });
  // 使用 useEffect 监听 fn 参数的变化,更新 当前目标函数
  useEffect(function () {
    current.fn = fn;
  }, [fn]);

  // 返回一个使用 useCallback 创建的节流函数 f
  return useCallback(function f(...args) {
    // 如果没有定时器,则设置一个定时器以延迟执行
    if (!current.timer) {
      current.timer = setTimeout(() => {
        // 清除定时器引用,允许下次触发
        delete current.timer;
      }, delay);

      // 执行最新的函数(current.fn)
      current.fn.call(this, ...args);
    }
  // 通过 dep 作为依赖项传递给 useCallback,以确保在依赖项变化时重新创建节流函数
  }, dep);
}
💡 思路:
  1. useThrottle 接受三个参数:

    • fn: 要节流的函数,即希望在指定时间间隔内执行的函数。
    • delay: 节流的时间间隔,以毫秒为单位。
    • dep(可选): 依赖数组,用于在 useCallback 内部的 useEffect 钩子中。它允许你控制节流函数的依赖项,以便在特定依赖项变化时重新创建节流函数。
  2. 使用 useRef 存储当前目标函数 fn 和定时器 timer 【用于禁止执行】

  3. 使用 useEffect 监听 fn 参数的变化。如果 fn 发生变化,将更新当前目标函数。

  4. 返回一个使用 useCallback 创建的函数 f,它是节流函数。

    1. f 函数内部,首先检查是否存在已经计划的定时器(current.timer)。如果没有定时器,说明已经过去了指定的间隔时间:

      • 创建一个新的定时器来禁止函数执行。在新定时器中,等待指定的 delay 时间后清除 current.timer 的引用,以允许下次触发。

      • 执行当前目标函数 current.fn,这确保了间隔时间内只有第一次触发 f 函数会实际执行函数。

    2. 最后,将 dep 作为依赖项传递给 useCallback,以确保在依赖项变化时重新创建节流函数。这是为了处理特殊情况,如果你想在某些依赖项变化时重置节流函数的状态,可以在 dep 数组中列出这些依赖项。