DebouncedComponent.tsx44 lines · main
1import { useEffect, useRef, useState } from 'react'
2
3interface DebouncedComponentProps {
4 value: any
5 delay?: number
6 fallback?: React.ReactNode
7 children: React.ReactNode
8}
9
10export function DebouncedComponent({
11 value,
12 delay = 500,
13 fallback = <div className="text-sm">Loading...</div>,
14 children,
15}: DebouncedComponentProps) {
16 const [shouldRender, setShouldRender] = useState(false)
17 const timeoutRef = useRef<NodeJS.Timeout>(null)
18 const prevValueRef = useRef(value)
19 const isInitialMount = useRef(true)
20
21 useEffect(() => {
22 if (isInitialMount.current || prevValueRef.current !== value) {
23 setShouldRender(false)
24 prevValueRef.current = value
25
26 if (timeoutRef.current) {
27 clearTimeout(timeoutRef.current)
28 }
29
30 timeoutRef.current = setTimeout(() => {
31 setShouldRender(true)
32 isInitialMount.current = false
33 }, delay)
34 }
35
36 return () => {
37 if (timeoutRef.current) {
38 clearTimeout(timeoutRef.current)
39 }
40 }
41 }, [value, delay])
42
43 return shouldRender ? children : fallback
44}