From 0940a4d1f575c29eb4d2aa5e48765236d3c32154 Mon Sep 17 00:00:00 2001 From: jeffvli Date: Fri, 17 Jul 2026 02:18:45 -0700 Subject: [PATCH] prevent images from blinking when src updates --- src/shared/components/image/use-native-image.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/shared/components/image/use-native-image.ts b/src/shared/components/image/use-native-image.ts index 6b1a5d171..3d114c1e7 100644 --- a/src/shared/components/image/use-native-image.ts +++ b/src/shared/components/image/use-native-image.ts @@ -25,6 +25,7 @@ export function useNativeImage({ const abortControllerRef = useRef(null); const loadedRequestSignatureRef = useRef(null); const objectUrlRef = useRef(null); + const retiredObjectUrlsRef = useRef([]); const onFetchErrorRef = useRef(onFetchError); const [state, setState] = useState({ status: 'idle' }); @@ -82,8 +83,10 @@ export function useNativeImage({ } abortCurrentRequest(); - revokeObjectUrl(); - setState({ status: 'loading' }); + setState((currentState) => ({ + displaySrc: currentState.displaySrc, + status: 'loading', + })); const abortController = new AbortController(); abortControllerRef.current = abortController; @@ -113,6 +116,9 @@ export function useNativeImage({ } const objectUrl = URL.createObjectURL(blob); + if (objectUrlRef.current) { + retiredObjectUrlsRef.current.push(objectUrlRef.current); + } objectUrlRef.current = objectUrl; loadedRequestSignatureRef.current = requestSignature; setState({ displaySrc: objectUrl, status: 'loaded' }); @@ -140,6 +146,11 @@ export function useNativeImage({ }; }, [enabled, fetchPriority, request, requestSignature]); + useEffect(() => { + retiredObjectUrlsRef.current.forEach((objectUrl) => URL.revokeObjectURL(objectUrl)); + retiredObjectUrlsRef.current = []; + }, [state.displaySrc]); + useEffect(() => { return () => { abortControllerRef.current?.abort(); @@ -147,6 +158,8 @@ export function useNativeImage({ if (objectUrlRef.current) { URL.revokeObjectURL(objectUrlRef.current); } + + retiredObjectUrlsRef.current.forEach((objectUrl) => URL.revokeObjectURL(objectUrl)); }; }, []);