Skip to content

Commit edd986b

Browse files
11koukoustyfle
authored andcommitted
Fix lazyRoot functionality for next/image (vercel#33933)
Fixed lazyRoot functionality (vercel#33290). Changed the unique id for Intersection Observers discrimination since previously they were only identified by the different rootMargins, now each being identified by the rootMargin and the root element as well Added more Images to the test with different margins and with/without lazyRoot prop. Browser correctly initially loading two of the four Images according to the props' specifications. Co-authored-by: Steven <steven@ceriously.com>
1 parent 38516e4 commit edd986b

File tree

4 files changed

+114
-41
lines changed

4 files changed

+114
-41
lines changed

packages/next/client/use-intersection.tsx

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@ type UseIntersection = { disabled?: boolean } & UseIntersectionObserverInit & {
1313
rootRef?: React.RefObject<HTMLElement> | null
1414
}
1515
type ObserveCallback = (isVisible: boolean) => void
16+
type Identifier = {
17+
root: Element | Document | null
18+
margin: string
19+
}
1620
type Observer = {
17-
id: string
21+
id: Identifier
1822
observer: IntersectionObserver
1923
elements: Map<Element, ObserveCallback>
2024
}
@@ -83,14 +87,35 @@ function observe(
8387
if (elements.size === 0) {
8488
observer.disconnect()
8589
observers.delete(id)
90+
let index = idList.findIndex(
91+
(obj) => obj.root === id.root && obj.margin === id.margin
92+
)
93+
if (index > -1) {
94+
idList.splice(index, 1)
95+
}
8696
}
8797
}
8898
}
8999

90-
const observers = new Map<string, Observer>()
100+
const observers = new Map<Identifier, Observer>()
101+
102+
const idList: Identifier[] = []
103+
91104
function createObserver(options: UseIntersectionObserverInit): Observer {
92-
const id = options.rootMargin || ''
93-
let instance = observers.get(id)
105+
const id = {
106+
root: options.root || null,
107+
margin: options.rootMargin || '',
108+
}
109+
let existing = idList.find(
110+
(obj) => obj.root === id.root && obj.margin === id.margin
111+
)
112+
let instance
113+
if (existing) {
114+
instance = observers.get(existing)
115+
} else {
116+
instance = observers.get(id)
117+
idList.push(id)
118+
}
94119
if (instance) {
95120
return instance
96121
}

test/integration/image-component/default/pages/lazy-noref.js

Lines changed: 0 additions & 32 deletions
This file was deleted.

test/integration/image-component/default/pages/lazy-withref.js

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,41 @@ const Page = () => {
1818
<div style={{ width: '400px', height: '600px' }}>hello</div>
1919
<div style={{ width: '400px', position: 'relative', height: '600px' }}>
2020
<Image
21-
lazyRoot={myRef}
22-
id="myImage"
21+
id="myImage1"
2322
src="/test.jpg"
23+
alt="mine"
24+
width="400"
25+
height="400"
26+
lazyBoundary="1500px"
27+
/>
28+
<Image
29+
lazyRoot={myRef}
30+
id="myImage2"
31+
src="/test.png"
32+
alt="mine"
33+
width="400"
34+
height="400"
35+
lazyBoundary="1800px"
36+
/>
37+
<Image
38+
lazyRoot={myRef}
39+
id="myImage3"
40+
src="/test.svg"
41+
alt="mine"
2442
width="400"
2543
height="400"
2644
lazyBoundary="1800px"
2745
/>
46+
47+
<Image
48+
lazyRoot={myRef}
49+
id="myImage4"
50+
src="/test.webp"
51+
alt="mine"
52+
width="400"
53+
height="400"
54+
lazyBoundary="200px"
55+
/>
2856
</div>
2957
</div>
3058
</>

test/integration/image-component/default/test/index.test.js

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,15 +1010,49 @@ function runTests(mode) {
10101010
}
10111011
})
10121012

1013-
it('should load the image when the lazyRoot prop is used', async () => {
1013+
it('should initially load only two of four images using lazyroot', async () => {
10141014
let browser
10151015
try {
1016-
//trying on '/lazy-noref' it fails
10171016
browser = await webdriver(appPort, '/lazy-withref')
1017+
await check(async () => {
1018+
const result = await browser.eval(
1019+
`document.getElementById('myImage1').naturalWidth`
1020+
)
1021+
1022+
if (result >= 400) {
1023+
throw new Error('Incorrectly loaded image')
1024+
}
1025+
1026+
return 'result-correct'
1027+
}, /result-correct/)
1028+
1029+
await check(async () => {
1030+
const result = await browser.eval(
1031+
`document.getElementById('myImage4').naturalWidth`
1032+
)
1033+
1034+
if (result >= 400) {
1035+
throw new Error('Incorrectly loaded image')
1036+
}
1037+
1038+
return 'result-correct'
1039+
}, /result-correct/)
10181040

10191041
await check(async () => {
10201042
const result = await browser.eval(
1021-
`document.getElementById('myImage').naturalWidth`
1043+
`document.getElementById('myImage2').naturalWidth`
1044+
)
1045+
1046+
if (result < 400) {
1047+
throw new Error('Incorrectly loaded image')
1048+
}
1049+
1050+
return 'result-correct'
1051+
}, /result-correct/)
1052+
1053+
await check(async () => {
1054+
const result = await browser.eval(
1055+
`document.getElementById('myImage3').naturalWidth`
10221056
)
10231057

10241058
if (result < 400) {
@@ -1033,7 +1067,25 @@ function runTests(mode) {
10331067
browser,
10341068
`http://localhost:${appPort}/_next/image?url=%2Ftest.jpg&w=828&q=75`
10351069
)
1070+
).toBe(false)
1071+
expect(
1072+
await hasImageMatchingUrl(
1073+
browser,
1074+
`http://localhost:${appPort}/_next/image?url=%2Ftest.png&w=828&q=75`
1075+
)
1076+
).toBe(true)
1077+
expect(
1078+
await hasImageMatchingUrl(
1079+
browser,
1080+
`http://localhost:${appPort}/_next/image?url=%2Ftest.svg&w=828&q=75`
1081+
)
10361082
).toBe(true)
1083+
expect(
1084+
await hasImageMatchingUrl(
1085+
browser,
1086+
`http://localhost:${appPort}/_next/image?url=%2Ftest.webp&w=828&q=75`
1087+
)
1088+
).toBe(false)
10371089
} finally {
10381090
if (browser) {
10391091
await browser.close()

0 commit comments

Comments
 (0)