Skip to content

Commit 0ea5af1

Browse files
authored
fix: Avoid unnecessary memory leaks due to prevExports (#766)
1 parent c8382ad commit 0ea5af1

File tree

1 file changed

+28
-13
lines changed

1 file changed

+28
-13
lines changed

lib/runtime/RefreshUtils.js

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,21 @@ function getReactRefreshBoundarySignature(moduleExports) {
6262
return signature;
6363
}
6464

65+
/**
66+
* Creates a data object to be retained across refreshes.
67+
* This object should not transtively reference previous exports,
68+
* which can form infinite chain of objects across refreshes, which can pressure RAM.
69+
*
70+
* @param {*} moduleExports A Webpack module exports object.
71+
* @returns {*} A React refresh boundary signature array.
72+
*/
73+
function getWebpackHotData(moduleExports) {
74+
return {
75+
signature: getReactRefreshBoundarySignature(moduleExports),
76+
isReactRefreshBoundary: isReactRefreshBoundary(moduleExports),
77+
};
78+
}
79+
6580
/**
6681
* Creates a helper that performs a delayed React refresh.
6782
* @returns {function(function(): void): void} A debounced React refresh function.
@@ -167,14 +182,11 @@ function registerExportsForReactRefresh(moduleExports, moduleId) {
167182
* Compares previous and next module objects to check for mutated boundaries.
168183
*
169184
* This implementation is based on the one in [Metro](https://github.com/facebook/metro/blob/907d6af22ac6ebe58572be418e9253a90665ecbd/packages/metro/src/lib/polyfills/require.js#L776-L792).
170-
* @param {*} prevExports The current Webpack module exports object.
171-
* @param {*} nextExports The next Webpack module exports object.
185+
* @param {*} prevSignature The signature of the current Webpack module exports object.
186+
* @param {*} nextSignature The signature of the next Webpack module exports object.
172187
* @returns {boolean} Whether the React refresh boundary should be invalidated.
173188
*/
174-
function shouldInvalidateReactRefreshBoundary(prevExports, nextExports) {
175-
var prevSignature = getReactRefreshBoundarySignature(prevExports);
176-
var nextSignature = getReactRefreshBoundarySignature(nextExports);
177-
189+
function shouldInvalidateReactRefreshBoundary(prevSignature, nextSignature) {
178190
if (prevSignature.length !== nextSignature.length) {
179191
return true;
180192
}
@@ -194,9 +206,9 @@ function executeRuntime(moduleExports, moduleId, webpackHot, refreshOverlay, isT
194206

195207
if (webpackHot) {
196208
var isHotUpdate = !!webpackHot.data;
197-
var prevExports;
209+
var prevData;
198210
if (isHotUpdate) {
199-
prevExports = webpackHot.data.prevExports;
211+
prevData = webpackHot.data.prevData;
200212
}
201213

202214
if (isReactRefreshBoundary(moduleExports)) {
@@ -209,7 +221,7 @@ function executeRuntime(moduleExports, moduleId, webpackHot, refreshOverlay, isT
209221
*/
210222
function hotDisposeCallback(data) {
211223
// We have to mutate the data object to get data registered and cached
212-
data.prevExports = moduleExports;
224+
data.prevData = getWebpackHotData(moduleExports);
213225
}
214226
);
215227
webpackHot.accept(
@@ -235,8 +247,12 @@ function executeRuntime(moduleExports, moduleId, webpackHot, refreshOverlay, isT
235247

236248
if (isHotUpdate) {
237249
if (
238-
isReactRefreshBoundary(prevExports) &&
239-
shouldInvalidateReactRefreshBoundary(prevExports, moduleExports)
250+
prevData &&
251+
prevData.isReactRefreshBoundary &&
252+
shouldInvalidateReactRefreshBoundary(
253+
prevData.signature,
254+
getReactRefreshBoundarySignature(moduleExports)
255+
)
240256
) {
241257
webpackHot.invalidate();
242258
} else {
@@ -254,7 +270,7 @@ function executeRuntime(moduleExports, moduleId, webpackHot, refreshOverlay, isT
254270
}
255271
}
256272
} else {
257-
if (isHotUpdate && typeof prevExports !== 'undefined') {
273+
if (isHotUpdate && typeof prevData !== 'undefined') {
258274
webpackHot.invalidate();
259275
}
260276
}
@@ -266,6 +282,5 @@ module.exports = Object.freeze({
266282
executeRuntime: executeRuntime,
267283
getModuleExports: getModuleExports,
268284
isReactRefreshBoundary: isReactRefreshBoundary,
269-
shouldInvalidateReactRefreshBoundary: shouldInvalidateReactRefreshBoundary,
270285
registerExportsForReactRefresh: registerExportsForReactRefresh,
271286
});

0 commit comments

Comments
 (0)