likes
comments
collection
share

React + TypeScript 封装公共组件

作者站长头像
站长
· 阅读数 18

防抖组件封装

使用场景

使用防抖的场景很多,其中一些例子包括:

  1. 搜索框输入联想功能:当用户输入搜索关键词时,如果搜索框每次输入都触发一次搜索接口调用,会导致频繁的网络请求和资源消耗。这时候可以使用防抖,设置一定的延迟时间,在用户输入之后等待一段时间再进行搜索。
  2. 滚动加载数据:当用户滚动页面到某个位置时,需要加载更多的数据。如果每次滚动都触发一次数据请求,会造成浏览器卡顿,影响用户体验。这时候可以使用防抖,设置一定的延迟时间,在用户停止滚动一段时间后再进行数据请求。
  3. 表单验证:当用户在表单中输入数据时,如果每次输入都进行实时的表单验证,同样会导致频繁的网络请求和资源消耗。这时候可以使用防抖,设置一定的延迟时间,在用户输入之后等待一段时间再进行表单验证。

总之,防抖技术可以在需要减少调用频率、减轻服务器压力、提高用户体验方面发挥积极的作用。

实现逻辑

下面这段代码是一个自定义 Hook,名为 useDebounce,用于实现防抖功能。在函数的参数中,value 表示需要防抖处理的值,delay 表示防抖的时间间隔,默认为 300 毫秒。该 Hook 利用 useState Hook 来存储经过防抖处理后的值,利用 useEffect Hook 来依赖 value 和 delay 的变化来重新执行,从而实现防抖。

每次在 value 变化后,setDebouncedValue 函数会延迟 delay 毫秒后执行,如果在这个时间间隔内又有新的 value 值,则会覆盖之前的定时器,重新开始计时。最终,经过防抖处理后的值将返回给调用该 Hook 的组件,从而实现防抖效果。

/**
* 自定义 Hook:用于实现 debounce(防抖)功能
* @param value - 需要防抖处理的值
* @param delay - 防抖的时间间隔,默认为 300ms
*/
export const useDebounce = <V>(value: V, delay?: number) => {
  // 使用 useState Hook 来存储经过防抖处理后的值
  const [debouncedValue, setDebouncedValue] = useState(value);
  // 使用 useEffect Hook,依赖 value 和 delay 的变化来重新执行
  useEffect(() => {
    // 每次在 value 变化以后,设置一个定时器
    const timeout = setTimeout(() => setDebouncedValue(value), delay);
    // 返回一个清除定时器的函数,保证每次在上一个 useEffect 处理完以后再执行
    return () => clearTimeout(timeout);
  }, [value, delay]);
  // 返回经过防抖处理后的值
  return debouncedValue;
};

使用场景

  1. 搜索框输入联想功能:
import React, { useState } from "react";
import { useDebounce } from "./useDebounce";

function Search() {
  const [searchTerm, setSearchTerm] = useState("");
  const debouncedSearchTerm = useDebounce(searchTerm, 300);
  
  useEffect(() => {
    // 发送搜索请求
    console.log("发送搜索请求:" + debouncedSearchTerm);
  }, [debouncedSearchTerm]);
  
  return (
    <div>
      <input
        type="text"
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
        placeholder="请输入搜索关键字"
      />
    </div>
  );
}

export default Search;

在上面的例子中,使用了自定义的 useDebounce Hook。组件中通过 useState Hook 来获取搜索框的输入值,然后传入 useDebounce Hook 实现防抖。在 useEffect Hook 中,监听防抖后的搜索关键字 debouncedSearchTerm 的变化,一旦发生变化就发送搜索请求,从而实现搜索框输入联想功能。

  1. 滚动加载数据:
import React, { useState, useEffect } from "react";
import { useDebounce } from "./useDebounce";

function Scroll() {
  const [list, setList] = useState([]);
  const [page, setPage] = useState(1);
  const debouncedPage = useDebounce(page, 300);
  
  useEffect(() => {
    // 发送数据请求
    console.log("发送数据请求:" + debouncedPage);
  }, [debouncedPage]);
  
  useEffect(() => {
    // 模拟数据请求
    setTimeout(() => {
      setList(prevList => prevList.concat(Array.from({length: 10}, (_, i) => i + (page - 1) * 10)));
    }, 1000);
  }, [page]);
  
  function handleScroll() {
    const { scrollTop, clientHeight, scrollHeight } = document.documentElement;
    if (scrollTop + clientHeight >= scrollHeight - 20) {
      setPage(prevPage => prevPage + 1);
    }
  }
  
  useEffect(() => {
    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  }, []);
  
  return (
    <ul>
      {list.map((item) => (
        <li key={item}>{item}</li>
      ))}
    </ul>
  );
}

export default Scroll;

**

在上面的例子中,使用了自定义的 useDebounce Hook。组件中通过 useState Hook 来保存数据列表和当前页数,并将当前页数传入 useDebounce Hook 实现防抖。在 useEffect Hook 中,监听防抖后的页数 debouncedPage 的变化,一旦发生变化就发送数据请求,从而实现滚动加载数据。同时,通过 useEffect Hook 监听 page 的变化,一旦发生变化就模拟数据请求并更新数据列表。在 handleScroll 函数中,监听窗口滚动事件,一旦滚动到页面底部则增加页数 page。最后渲染数据列表。

节流useThrottle

该 hooks 接受两个参数:第一个参数是要进行节流的值,第二个参数是节流的时间间隔。

在这个 hooks 里,我们使用了 useState 来保存节流后的值,然后使用 useEffect 来实现节流逻辑。useEffect 会在 value 或 delay 发生变化时被调用,然后我们使用 setTimeout 来延迟响应,并在延迟时间到达后将值设置为节流后的值。

最后,我们将节流后的值 return 出去,方便组件里面的使用。

import { useEffect, useState } from 'react';

/**
 * 自定义的 useThrottle hooks
 * @param value - 需要进行节流的值
 * @param delay - 节流的时间间隔
 * @returns 节流后的值
 */
function useThrottle<T>(value: T, delay: number): T {
  const [throttledValue, setThrottledValue] = useState(value); // 保存节流后的值
  useEffect(() => {
    let timeout: ReturnType<typeof setTimeout> | null = null; // 保存 setTimeout 返回值,方便清空
    /**
     * 延时响应函数
     */
    const handleTimeout = () => {
      setThrottledValue(value); // 触发节流后的值更新
      timeout = null; // 清空 timeout,避免多次触发
    }
    if (timeout === null) { // 仅当 timeout 不存在时才触发
      timeout = setTimeout(handleTimeout, delay); // 延迟(delay)时间后触发 handleTimeout
    }
    return () => {
      if (timeout !== null) { // 如果 timeout 存在,则清空
        clearTimeout(timeout);
        timeout = null;
      }
    }
  }, [value, delay]); // 监听传入的参数 value 和 delay
  return throttledValue;
}
export default useThrottle;

使用场景

function Example() {
  const [inputValue, setInputValue] = useState('');
  const throttledInputValue = useThrottle(inputValue, 500);

  return (
    <div>
      <input value={inputValue} onChange={(e) => setInputValue(e.target.value)} />
      <p>Throttled value: {throttledInputValue}</p>
    </div>
  );
}