React Hooks --- 分享自己开发中常用的自定义的Hooks (1)

为什么要使用自定义 Hooks

自定义 Hooks 是 React 中一种复用逻辑的机制,通过它们可以抽离组件中的逻辑,使代码更加简洁、易读、易维护。它们可以在多个组件中复用相同的逻辑,减少重复代码。

1、useThrottle

代码
javascript 复制代码
import React,{ useRef, useState,useEffect } from "react";

/**
 * useThrottle:一个节流的 hook,用于限制状态更新的频率。
 *
 * @param {any} initialState 初始状态
 * @param {number} delay 节流间隔时间,默认为 500 毫秒
 * @returns {any} 节流后的状态
 */
export const useThrottle = (initialState, delay = 5000) => {
  const [state, setState] = useState(initialState);
  const timeout = useRef();
  const nextValue = useRef(null);
  const hasNextValue = useRef(false);

  useEffect(() => {
    if (timeout.current) {
      nextValue.current = initialState;
      hasNextValue.current = true;
    } else {
      setState(initialState);
      const timeoutCallback = () => {
        if (hasNextValue.current) {
          setState(nextValue.current);
          hasNextValue.current = false;
        }
        timeout.current = undefined;
      };
      timeout.current = setTimeout(timeoutCallback, delay);
    }
    return () => {
        timeout.current && clearTimeout(timeout.current);
    }
  }, [initialState]);

  return state;
};
用法
javascript 复制代码
import { useThrottle } from './useThrottle';

const value = useThrottle(state, 500);

2、useVirtual

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

/**
 * useVirtual:一个虚拟滚动的 hook,用于优化长列表的渲染性能。
 *
 * @param {object} listRef 列表的引用对象
 * @param {Array} list 初始列表数据
 * @param {boolean} isFullScreen 是否全屏显示
 * @returns {Array} 显示在视图中的列表数据和 padding 样式
 */
export const useVirtual = (listRef, list, isFullScreen) => {
  const origin = list;
  let viewHeight = null;
  let itemHeight = 0;
  let dur = 0;
  const rootFontSize = parseInt(document.documentElement.style.fontSize);

  const [viewList, setViewList] = useState(list);
  const [startIndex, setStartIndex] = useState(0);
  const [endIndex, setEndIndex] = useState(0);
  const [padding, setPadding] = useState({
    paddingTop: 0,
    paddingBottom: 0,
  });

  useEffect(() => {
    init(listRef);
  }, []);

  useEffect(() => {
    initData(listRef.current);
    update();
  }, [startIndex]);

  function init(ref) {
    initData(ref.current);
    render(startIndex, dur + 1);
    eventBind(ref.current);
  }

  function initData(dom) {
    const target = isFullScreen ? document.documentElement : dom;
    viewHeight = isFullScreen ? target.offsetHeight : target.parentNode.offsetHeight;
    itemHeight = target.getElementsByClassName('virtual-item')[0].offsetHeight;
    dur = Math.floor(viewHeight / itemHeight);
  }

  function eventBind(dom) {
    const eventTarget = isFullScreen ? window : dom.parentNode;
    eventTarget.addEventListener('scroll', handleScroll, false);
  }

  function render(startIndex, endIndex) {
    setViewList(() => origin.slice(startIndex, endIndex));
    setEndIndex(() => startIndex + dur + 1);
  }

  function handleScroll(e) {
    e.stopPropagation();
    const target = isFullScreen ? document.documentElement : listRef.current.parentNode;
    setStartIndex(() => Math.floor(target.scrollTop / itemHeight));
  }

  function update() {
    if (startIndex === endIndex) return;
    setEndIndex(() => startIndex + dur);
    render(startIndex, endIndex);
    setPadding(() => {
      return {
        paddingTop: (startIndex * itemHeight) / rootFontSize,
        paddingBottom: ((origin.length - endIndex) * itemHeight) / rootFontSize,
      };
    });
  }

  return [viewList, padding];
};
用法
javascript 复制代码
import { useRef } from 'react';
import { useVirtual } from './useVirtual';

const listRef = useRef();
const [viewList, padding] = useVirtual(listRef, initialList, false);

3、useCopyToClipboard

代码
javascript 复制代码
import { message } from 'antd';

/**
 * useCopyToClipboard:一个 hook,用于复制文本到剪贴板。
 *
 * @returns {Array} 包含单个函数 `handleCopy`,

用于复制文本到剪贴板
 */
const useCopyToClipboard = () => {
  function handleCopy(text) {
    if (text) {
      let input = document.createElement('input');
      input.type = 'text';
      input.value = text;
      input.style.position = 'fixed';
      input.style.opacity = '0';
      document.body.appendChild(input);
      input.select();
      if (document.execCommand('copy')) {
        message.success('复制成功');
      } else {
        message.error('复制失败');
      }
      document.body.removeChild(input);
    } else {
      message.error('复制失败');
    }
  }

  return [handleCopy];
};

export default useCopyToClipboard;
用法
javascript 复制代码
import useCopyToClipboard from './useCopyToClipboard';

const [handleCopy] = useCopyToClipboard();

const copyText = () => {
  handleCopy('需要复制的文本');
};

4、useSmoothEnter

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

/**
 * useSmoothEnter:一个 hook,在 DOM 元素进入视口时添加平滑的进入动画。
 *
 * @returns {object} ref - 一个可以附加到 DOM 元素的 ref 对象,用于动画效果
 */
const useSmoothEnter = () => {
  const ref = useRef(null);
  const [isVisible, setIsVisible] = useState(false);
  const [animationStyle, setAnimationStyle] = useState({
    animation: '',
    animationPlayState: 'paused',
  });

  useEffect(() => {
    const observer = new IntersectionObserver((entries) => {
      const [entry] = entries;
      setIsVisible(entry.isIntersecting);
    });

    const element = ref.current;

    if (element) {
      observer.observe(element);

      return () => {
        observer.unobserve(element);
      };
    }
  }, [ref]);

  useEffect(() => {
    if (isVisible) {
      setAnimationStyle({
        animation: 'enterAnimation 1s ease',
        animationPlayState: 'running',
      });
    }
  }, [isVisible]);

  useEffect(() => {
    const element = ref.current;

    if (element) {
      element.style.animation = animationStyle.animation;
      element.style.animationPlayState = animationStyle.animationPlayState;
    }
  }, [animationStyle]);

  return ref;
};

export default useSmoothEnter;
用法
javascript 复制代码
import useSmoothEnter from './useSmoothEnter';

const ref = useSmoothEnter();

return <div ref={ref} className="animated-element">内容</div>;
相关推荐
f89790707019 分钟前
layui动态表格出现 横竖间隔线
前端·javascript·layui
鱼跃鹰飞25 分钟前
Leecode热题100-295.数据流中的中位数
java·服务器·开发语言·前端·算法·leetcode·面试
二十雨辰1 小时前
[uni-app]小兔鲜-04推荐+分类+详情
前端·javascript·uni-app
霸王蟹2 小时前
Vue3 项目中为啥不需要根标签了?
前端·javascript·vue.js·笔记·学习
小白求学12 小时前
CSS计数器
前端·css
Anita_Sun2 小时前
🌈 Git 全攻略 - Git 的初始设置 ✨
前端
lucifer3112 小时前
深入解析 React 组件封装 —— 从业务需求到性能优化
前端·react.js
等什么君!2 小时前
复习HTML(进阶)
前端·html
儒雅的烤地瓜2 小时前
JS | 如何解决ajax无法后退的问题?
前端·javascript·ajax·pushstate·popstate事件·replacestate
觉醒法师2 小时前
Vue3+TS项目 - ref和useTemplateRef获取组件实例
开发语言·前端·javascript