Skip to content

Commit 9298fa3

Browse files
fix(gatsby-image): fix memory leak and use more appropriate data structures for cache and listeners (#10278)
* fix(gatsby-image): fix memory leak and use more appropriate data structures for cache and listeners * fix(gatsby-image): remove from listeners when unmounting * fix(gatsby-image): fix ref reference for fixed images * nits * Revert propType changes * Fix issues after merge * Fix cache bug after merge * set -> add * Cleanup observer on unmount * remove → delete * remove refs Co-authored-by: Ward Peeters <ward@coding-tech.com>
1 parent 88b3158 commit 9298fa3

1 file changed

Lines changed: 29 additions & 14 deletions

File tree

packages/gatsby-image/src/index.js

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const convertProps = props => {
1818

1919
// Cache if we've seen an image before so we don't bother with
2020
// lazy-loading & fading in on subsequent mounts.
21-
const imageCache = {}
21+
const imageCache = Object.create({})
2222
const inImageCache = props => {
2323
const convertedProps = convertProps(props)
2424
// Find src
@@ -40,7 +40,7 @@ const activateCacheForImage = props => {
4040
}
4141

4242
let io
43-
const listeners = []
43+
const listeners = new WeakMap()
4444

4545
function getIO() {
4646
if (
@@ -51,15 +51,15 @@ function getIO() {
5151
io = new window.IntersectionObserver(
5252
entries => {
5353
entries.forEach(entry => {
54-
listeners.forEach(l => {
55-
if (l[0] === entry.target) {
56-
// Edge doesn't currently support isIntersecting, so also test for an intersectionRatio > 0
57-
if (entry.isIntersecting || entry.intersectionRatio > 0) {
58-
io.unobserve(l[0])
59-
l[1]()
60-
}
54+
if (listeners.has(entry.target)) {
55+
const cb = listeners.get(entry.target)
56+
// Edge doesn't currently support isIntersecting, so also test for an intersectionRatio > 0
57+
if (entry.isIntersecting || entry.intersectionRatio > 0) {
58+
io.unobserve(entry.target)
59+
listeners.delete(entry.target)
60+
cb()
6161
}
62-
})
62+
}
6363
})
6464
},
6565
{ rootMargin: `200px` }
@@ -70,8 +70,17 @@ function getIO() {
7070
}
7171

7272
const listenToIntersections = (el, cb) => {
73-
getIO().observe(el)
74-
listeners.push([el, cb])
73+
const observer = getIO()
74+
75+
if (observer) {
76+
observer.observe(el)
77+
listeners.set(el, cb)
78+
}
79+
80+
return () => {
81+
observer.unobserve(el)
82+
listeners.delete(el)
83+
}
7584
}
7685

7786
const noscriptImg = props => {
@@ -159,7 +168,7 @@ class Image extends React.Component {
159168
IOSupported = false
160169
}
161170

162-
const hasNoScript = !(this.props.critical && !this.props.fadeIn)
171+
const hasNoScript = !(props.critical && !props.fadeIn)
163172

164173
this.state = {
165174
isVisible,
@@ -187,9 +196,15 @@ class Image extends React.Component {
187196
}
188197
}
189198

199+
componentWillUnmount() {
200+
if (this.cleanUpListeners) {
201+
this.cleanUpListeners()
202+
}
203+
}
204+
190205
handleRef(ref) {
191206
if (this.state.IOSupported && ref) {
192-
listenToIntersections(ref, () => {
207+
this.cleanUpListeners = listenToIntersections(ref, () => {
193208
const imageInCache = inImageCache(this.props)
194209
if (
195210
!this.state.isVisible &&

0 commit comments

Comments
 (0)