DebouncedComponent.tsx 1.0 KB

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