React + TypeScript 封装公共组件
防抖组件封装
使用场景
使用防抖的场景很多,其中一些例子包括:
- 搜索框输入联想功能:当用户输入搜索关键词时,如果搜索框每次输入都触发一次搜索接口调用,会导致频繁的网络请求和资源消耗。这时候可以使用防抖,设置一定的延迟时间,在用户输入之后等待一段时间再进行搜索。
- 滚动加载数据:当用户滚动页面到某个位置时,需要加载更多的数据。如果每次滚动都触发一次数据请求,会造成浏览器卡顿,影响用户体验。这时候可以使用防抖,设置一定的延迟时间,在用户停止滚动一段时间后再进行数据请求。
- 表单验证:当用户在表单中输入数据时,如果每次输入都进行实时的表单验证,同样会导致频繁的网络请求和资源消耗。这时候可以使用防抖,设置一定的延迟时间,在用户输入之后等待一段时间再进行表单验证。
总之,防抖技术可以在需要减少调用频率、减轻服务器压力、提高用户体验方面发挥积极的作用。
实现逻辑
下面这段代码是一个自定义 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;
};
使用场景
- 搜索框输入联想功能:
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 的变化,一旦发生变化就发送搜索请求,从而实现搜索框输入联想功能。
- 滚动加载数据:
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>
);
}
转载自:https://juejin.cn/post/7229929532496134181