From 23990b581b7cfbd379efcdc7161da1fc976b80ba Mon Sep 17 00:00:00 2001 From: jeffvli Date: Sat, 24 Jan 2026 18:08:44 -0800 Subject: [PATCH] add enableDebounce prop to Image to conditionally debounce image loading --- src/shared/components/image/image.tsx | 107 +++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 2 deletions(-) diff --git a/src/shared/components/image/image.tsx b/src/shared/components/image/image.tsx index 23591b274..e72c493d1 100644 --- a/src/shared/components/image/image.tsx +++ b/src/shared/components/image/image.tsx @@ -13,12 +13,15 @@ import styles from './image.module.css'; import { AppIcon, Icon } from '/@/shared/components/icon/icon'; import { Skeleton } from '/@/shared/components/skeleton/skeleton'; +import { useDebouncedValue } from '/@/shared/hooks/use-debounced-value'; import { useInViewport } from '/@/shared/hooks/use-in-viewport'; export interface ImageProps extends Omit, 'src'> { containerClassName?: string; enableAnimation?: boolean; + enableDebounce?: boolean; enableViewport?: boolean; + fetchPriority?: 'auto' | 'high' | 'low'; imageContainerProps?: Omit; includeLoader?: boolean; includeUnloader?: boolean; @@ -48,7 +51,9 @@ export function BaseImage({ className, containerClassName, enableAnimation = false, + enableDebounce = true, enableViewport = true, + fetchPriority, imageContainerProps, includeLoader = true, includeUnloader = true, @@ -56,6 +61,23 @@ export function BaseImage({ unloaderIcon = 'emptyImage', ...props }: ImageProps) { + if (enableDebounce) { + return ( + + ); + } + if (enableViewport) { return ( : null} loading="eager" src={src} @@ -95,6 +117,86 @@ export function BaseImage({ } {...props} /> + ) : ( + + )} + + ); +} + +function ImageWithDebounce({ + className, + containerClassName, + enableAnimation, + enableViewport, + fetchPriority, + imageContainerProps, + includeLoader, + includeUnloader, + src, + unloaderIcon, + ...props +}: ImageProps) { + const [debouncedSrc] = useDebouncedValue(src, 150, { waitForInitial: true }); + const viewport = useInViewport(); + const { inViewport, ref } = enableViewport ? viewport : { inViewport: true, ref: undefined }; + + if (enableViewport) { + return ( + + {inViewport && debouncedSrc ? ( + : null} + loading="eager" + src={debouncedSrc} + unloader={ + includeUnloader ? ( + + ) : null + } + {...props} + /> + ) : !src ? ( + + ) : ( + + )} + + ); + } + + return ( + + {debouncedSrc ? ( + : null} + src={debouncedSrc} + unloader={ + includeUnloader ? ( + + ) : null + } + {...props} + /> ) : !src ? ( ) : ( @@ -108,6 +210,7 @@ function ImageWithViewport({ className, containerClassName, enableAnimation, + fetchPriority, imageContainerProps, includeLoader, includeUnloader, @@ -130,7 +233,7 @@ function ImageWithViewport({ [styles.animated]: enableAnimation, })} decoding="async" - fetchPriority="high" + fetchPriority={fetchPriority} loader={includeLoader ? : null} loading="eager" src={src}