1- import React from 'react'
1+ import React , { useRef , useEffect } from 'react'
22import Head from '../shared/lib/head'
33import {
44 ImageConfigComplete ,
88} from '../server/image-config'
99import { useIntersection } from './use-intersection'
1010
11- const loadedImageURLs = new Set < string > ( )
1211const allImgs = new Map <
1312 string ,
1413 { src : string ; priority : boolean ; placeholder : string }
@@ -250,31 +249,34 @@ function defaultImageLoader(loaderProps: ImageLoaderProps) {
250249// See https://stackoverflow.com/q/39777833/266535 for why we use this ref
251250// handler instead of the img's onLoad attribute.
252251function handleLoading (
253- img : HTMLImageElement | null ,
252+ imgRef : React . RefObject < HTMLImageElement > ,
254253 src : string ,
255254 layout : LayoutValue ,
256255 placeholder : PlaceholderValue ,
257- onLoadingComplete ?: OnLoadingComplete
256+ onLoadingCompleteRef : React . MutableRefObject < OnLoadingComplete | undefined >
258257) {
259- if ( ! img ) {
260- return
261- }
262258 const handleLoad = ( ) => {
259+ const img = imgRef . current
260+ if ( ! img ) {
261+ return
262+ }
263263 if ( img . src !== emptyDataURL ) {
264264 const p = 'decode' in img ? img . decode ( ) : Promise . resolve ( )
265265 p . catch ( ( ) => { } ) . then ( ( ) => {
266+ if ( ! imgRef . current ) {
267+ return
268+ }
266269 if ( placeholder === 'blur' ) {
267270 img . style . filter = ''
268271 img . style . backgroundSize = ''
269272 img . style . backgroundImage = ''
270273 img . style . backgroundPosition = ''
271274 }
272- loadedImageURLs . add ( src )
273- if ( onLoadingComplete ) {
275+ if ( onLoadingCompleteRef . current ) {
274276 const { naturalWidth, naturalHeight } = img
275277 // Pass back read-only primitive values but not the
276278 // underlying DOM element because it could be misused.
277- onLoadingComplete ( { naturalWidth, naturalHeight } )
279+ onLoadingCompleteRef . current ( { naturalWidth, naturalHeight } )
278280 }
279281 if ( process . env . NODE_ENV !== 'production' ) {
280282 if ( img . parentElement ?. parentElement ) {
@@ -299,13 +301,15 @@ function handleLoading(
299301 } )
300302 }
301303 }
302- if ( img . complete ) {
303- // If the real image fails to load, this will still remove the placeholder.
304- // This is the desired behavior for now, and will be revisited when error
305- // handling is worked on for the image component itself.
306- handleLoad ( )
307- } else {
308- img . onload = handleLoad
304+ if ( imgRef . current ) {
305+ if ( imgRef . current . complete ) {
306+ // If the real image fails to load, this will still remove the placeholder.
307+ // This is the desired behavior for now, and will be revisited when error
308+ // handling is worked on for the image component itself.
309+ handleLoad ( )
310+ } else {
311+ imgRef . current . onload = handleLoad
312+ }
309313 }
310314}
311315
@@ -328,6 +332,7 @@ export default function Image({
328332 blurDataURL,
329333 ...all
330334} : ImageProps ) {
335+ const imgRef = useRef < HTMLImageElement > ( null )
331336 let rest : Partial < ImageProps > = all
332337 let layout : NonNullable < LayoutValue > = sizes ? 'responsive' : 'intrinsic'
333338 if ( 'layout' in rest ) {
@@ -376,7 +381,7 @@ export default function Image({
376381 unoptimized = true
377382 isLazy = false
378383 }
379- if ( typeof window !== 'undefined' && loadedImageURLs . has ( src ) ) {
384+ if ( typeof window !== 'undefined' && imgRef . current ?. complete ) {
380385 isLazy = false
381386 }
382387
@@ -504,7 +509,7 @@ export default function Image({
504509 }
505510 }
506511
507- const [ setRef , isIntersected ] = useIntersection < HTMLImageElement > ( {
512+ const [ setIntersection , isIntersected ] = useIntersection < HTMLImageElement > ( {
508513 rootMargin : lazyBoundary ,
509514 disabled : ! isLazy ,
510515 } )
@@ -657,6 +662,22 @@ export default function Image({
657662 [ imageSizesPropName ] : imgAttributes . sizes ,
658663 }
659664
665+ const useLayoutEffect =
666+ typeof window === 'undefined' ? React . useEffect : React . useLayoutEffect
667+ const onLoadingCompleteRef = useRef ( onLoadingComplete )
668+
669+ useEffect ( ( ) => {
670+ onLoadingCompleteRef . current = onLoadingComplete
671+ } , [ onLoadingComplete ] )
672+
673+ useLayoutEffect ( ( ) => {
674+ setIntersection ( imgRef . current )
675+ } , [ setIntersection ] )
676+
677+ useEffect ( ( ) => {
678+ handleLoading ( imgRef , srcString , layout , placeholder , onLoadingCompleteRef )
679+ } , [ srcString , layout , placeholder , isVisible ] )
680+
660681 return (
661682 < span style = { wrapperStyle } >
662683 { hasSizer ? (
@@ -687,10 +708,7 @@ export default function Image({
687708 decoding = "async"
688709 data-nimg = { layout }
689710 className = { className }
690- ref = { ( img ) => {
691- setRef ( img )
692- handleLoading ( img , srcString , layout , placeholder , onLoadingComplete )
693- } }
711+ ref = { imgRef }
694712 style = { { ...imgStyle , ...blurStyle } }
695713 />
696714 { isLazy && (
0 commit comments