From 746951b55fb8866e73c1ca141f7d9a2957f3820a Mon Sep 17 00:00:00 2001 From: jeffvli Date: Sat, 10 Jan 2026 14:22:05 -0800 Subject: [PATCH] refactor image to only render in viewport once --- src/shared/components/image/image.tsx | 87 ++++++++++++++++++--------- 1 file changed, 57 insertions(+), 30 deletions(-) diff --git a/src/shared/components/image/image.tsx b/src/shared/components/image/image.tsx index 6cb49aeb8..ce9b96218 100644 --- a/src/shared/components/image/image.tsx +++ b/src/shared/components/image/image.tsx @@ -6,6 +6,8 @@ import { type ImgHTMLAttributes, memo, ReactNode, + useEffect, + useState, } from 'react'; import { Img } from 'react-image'; @@ -43,6 +45,25 @@ interface ImageUnloaderProps { export const FALLBACK_SVG = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMDAiIGhlaWdodD0iMzAwIj48ZmlsdGVyIGlkPSJhIiB4PSIwIiB5PSIwIj48ZmVUdXJidWxlbmNlIHR5cGU9ImZyYWN0YWxOb2lzZSIgYmFzZUZyZXF1ZW5jeT0iLjc1IiBzdGl0Y2hUaWxlcz0ic3RpdGNoIi8+PGZlQ29sb3JNYXRyaXggdHlwZT0ic2F0dXJhdGUiIHZhbHVlcz0iMCIvPjwvZmlsdGVyPjxwYXRoIGZpbHRlcj0idXJsKCNhKSIgb3BhY2l0eT0iLjA1IiBkPSJNMCAwaDMwMHYzMDBIMHoiLz48L3N2Zz4='; +interface ImageViewportWrapperProps { + children: (shouldRenderImage: boolean, ref: ForwardedRef) => ReactNode; +} + +const ImageViewportWrapper = ({ children }: ImageViewportWrapperProps) => { + const [hasEnteredViewport, setHasEnteredViewport] = useState(false); + const { inViewport, ref } = useInViewport(); + + useEffect(() => { + if (inViewport && !hasEnteredViewport) { + setHasEnteredViewport(true); + } + }, [inViewport, hasEnteredViewport]); + + const shouldRenderImage = hasEnteredViewport || inViewport; + + return <>{children(shouldRenderImage, ref)}; +}; + export function BaseImage({ className, containerClassName, @@ -54,38 +75,44 @@ export function BaseImage({ unloaderIcon = 'emptyImage', ...props }: ImageProps) { - const { inViewport, ref } = useInViewport(); - return ( - - {inViewport && src ? ( - : null} - loading="eager" - src={src} - unloader={ - includeUnloader ? ( + + {(shouldRenderImage, viewportRef) => { + return ( + + {shouldRenderImage && src ? ( + : null + } + loading="eager" + src={src} + unloader={ + includeUnloader ? ( + + ) : null + } + {...props} + /> + ) : !src ? ( - ) : null - } - {...props} - /> - ) : !src ? ( - - ) : ( - - )} - + ) : ( + + )} + + ); + }} + ); }