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>;
相关推荐
zwjapple1 小时前
docker-compose一键部署全栈项目。springboot后端,react前端
前端·spring boot·docker
像风一样自由20204 小时前
HTML与JavaScript:构建动态交互式Web页面的基石
前端·javascript·html
伍哥的传说4 小时前
React 各颜色转换方法、颜色值换算工具HEX、RGB/RGBA、HSL/HSLA、HSV、CMYK
深度学习·神经网络·react.js
aiprtem4 小时前
基于Flutter的web登录设计
前端·flutter
浪裡遊4 小时前
React Hooks全面解析:从基础到高级的实用指南
开发语言·前端·javascript·react.js·node.js·ecmascript·php
why技术4 小时前
Stack Overflow,轰然倒下!
前端·人工智能·后端
GISer_Jing5 小时前
0704-0706上海,又聚上了
前端·新浪微博
止观止5 小时前
深入探索 pnpm:高效磁盘利用与灵活的包管理解决方案
前端·pnpm·前端工程化·包管理器
whale fall5 小时前
npm install安装的node_modules是什么
前端·npm·node.js
烛阴5 小时前
简单入门Python装饰器
前端·python