@@ -35,8 +35,7 @@ import {
35
35
enableSuspenseCallback ,
36
36
enableScopeAPI ,
37
37
enableStrictEffects ,
38
- enableStrongMemoryCleanup ,
39
- enableDetachOldChildList ,
38
+ deletedTreeCleanUpLevel ,
40
39
} from 'shared/ReactFeatureFlags' ;
41
40
import {
42
41
FunctionComponent ,
@@ -1212,40 +1211,93 @@ function detachFiberMutation(fiber: Fiber) {
1212
1211
// Don't reset the alternate yet, either. We need that so we can detach the
1213
1212
// alternate's fields in the passive phase. Clearing the return pointer is
1214
1213
// sufficient for findDOMNode semantics.
1214
+ const alternate = fiber . alternate ;
1215
+ if ( alternate !== null ) {
1216
+ alternate . return = null ;
1217
+ }
1215
1218
fiber . return = null ;
1216
1219
}
1217
1220
1218
- export function detachFiberAfterEffects (
1219
- fiber : Fiber ,
1220
- recurseIntoSibbling : ?boolean ,
1221
- ) : void {
1222
- // Null out fields to improve GC for references that may be lingering (e.g. DevTools).
1223
- // Note that we already cleared the return pointer in detachFiberMutation().
1224
- if ( enableStrongMemoryCleanup ) {
1225
- if ( fiber . child ) {
1226
- detachFiberAfterEffects ( fiber . child , true ) ;
1227
- }
1228
- if ( fiber . sibling && recurseIntoSibbling === true ) {
1229
- detachFiberAfterEffects ( fiber . sibling , true ) ;
1230
- }
1231
- if ( fiber . stateNode ) {
1232
- unmountNode ( fiber . stateNode ) ;
1233
- }
1234
- fiber . return = null ;
1235
- }
1236
- fiber . alternate = null ;
1237
- fiber . child = null ;
1238
- fiber . deletions = null ;
1239
- fiber . dependencies = null ;
1240
- fiber . memoizedProps = null ;
1241
- fiber . memoizedState = null ;
1242
- fiber . pendingProps = null ;
1243
- fiber . sibling = null ;
1244
- fiber . stateNode = null ;
1245
- fiber . updateQueue = null ;
1221
+ function detachFiberAfterEffects ( fiber : Fiber ) {
1222
+ const alternate = fiber . alternate ;
1223
+ if ( alternate !== null ) {
1224
+ fiber . alternate = null ;
1225
+ detachFiberAfterEffects ( alternate ) ;
1226
+ }
1227
+
1228
+ // Note: Defensively using negation instead of < in case
1229
+ // `deletedTreeCleanUpLevel` is undefined.
1230
+ if ( ! ( deletedTreeCleanUpLevel >= 2 ) ) {
1231
+ // This is the default branch (level 0). We do not recursively clear all the
1232
+ // fiber fields. Only the root of the deleted subtree.
1233
+ fiber . child = null ;
1234
+ fiber . deletions = null ;
1235
+ fiber . dependencies = null ;
1236
+ fiber . memoizedProps = null ;
1237
+ fiber . memoizedState = null ;
1238
+ fiber . pendingProps = null ;
1239
+ fiber . sibling = null ;
1240
+ fiber . stateNode = null ;
1241
+ fiber . updateQueue = null ;
1246
1242
1247
- if ( __DEV__ ) {
1248
- fiber . _debugOwner = null ;
1243
+ if ( __DEV__ ) {
1244
+ fiber . _debugOwner = null ;
1245
+ }
1246
+ } else {
1247
+ // Recursively traverse the entire deleted tree and clean up fiber fields.
1248
+ // This is more aggressive than ideal, and the long term goal is to only
1249
+ // have to detach the deleted tree at the root.
1250
+ let child = fiber . child ;
1251
+ while ( child !== null ) {
1252
+ detachFiberAfterEffects ( child ) ;
1253
+ child = child . sibling ;
1254
+ }
1255
+ // Clear cyclical Fiber fields. This level alone is designed to roughly
1256
+ // approximate the planned Fiber refactor. In that world, `setState` will be
1257
+ // bound to a special "instance" object instead of a Fiber. The Instance
1258
+ // object will not have any of these fields. It will only be connected to
1259
+ // the fiber tree via a single link at the root. So if this level alone is
1260
+ // sufficient to fix memory issues, that bodes well for our plans.
1261
+ fiber . child = null ;
1262
+ fiber . deletions = null ;
1263
+ fiber . sibling = null ;
1264
+
1265
+ // I'm intentionally not clearing the `return` field in this level. We
1266
+ // already disconnect the `return` pointer at the root of the deleted
1267
+ // subtree (in `detachFiberMutation`). Besides, `return` by itself is not
1268
+ // cyclical — it's only cyclical when combined with `child`, `sibling`, and
1269
+ // `alternate`. But we'll clear it in the next level anyway, just in case.
1270
+
1271
+ if ( __DEV__ ) {
1272
+ fiber . _debugOwner = null ;
1273
+ }
1274
+
1275
+ if ( deletedTreeCleanUpLevel >= 3 ) {
1276
+ // Theoretically, nothing in here should be necessary, because we already
1277
+ // disconnected the fiber from the tree. So even if something leaks this
1278
+ // particular fiber, it won't leak anything else
1279
+ //
1280
+ // The purpose of this branch is to be super aggressive so we can measure
1281
+ // if there's any difference in memory impact. If there is, that could
1282
+ // indicate a React leak we don't know about.
1283
+
1284
+ // For host components, disconnect host instance -> fiber pointer.
1285
+ if ( fiber . tag === HostComponent ) {
1286
+ const hostInstance : Instance = fiber . stateNode ;
1287
+ if ( hostInstance !== null ) {
1288
+ unmountNode ( hostInstance ) ;
1289
+ }
1290
+ }
1291
+
1292
+ fiber . return = null ;
1293
+ fiber . dependencies = null ;
1294
+ fiber . memoizedProps = null ;
1295
+ fiber . memoizedState = null ;
1296
+ fiber . pendingProps = null ;
1297
+ fiber . stateNode = null ;
1298
+ // TODO: Move to `commitPassiveUnmountInsideDeletedTreeOnFiber` instead.
1299
+ fiber . updateQueue = null ;
1300
+ }
1249
1301
}
1250
1302
}
1251
1303
@@ -1637,11 +1689,8 @@ function commitDeletion(
1637
1689
renderPriorityLevel ,
1638
1690
) ;
1639
1691
}
1640
- const alternate = current . alternate ;
1692
+
1641
1693
detachFiberMutation ( current ) ;
1642
- if ( alternate !== null ) {
1643
- detachFiberMutation ( alternate ) ;
1644
- }
1645
1694
}
1646
1695
1647
1696
function commitWork ( current : Fiber | null , finishedWork : Fiber ) : void {
@@ -2318,14 +2367,10 @@ function commitPassiveUnmountEffects_begin() {
2318
2367
) ;
2319
2368
2320
2369
// Now that passive effects have been processed, it's safe to detach lingering pointers.
2321
- const alternate = fiberToDelete . alternate ;
2322
2370
detachFiberAfterEffects ( fiberToDelete ) ;
2323
- if ( alternate !== null ) {
2324
- detachFiberAfterEffects ( alternate ) ;
2325
- }
2326
2371
}
2327
2372
2328
- if ( enableDetachOldChildList ) {
2373
+ if ( deletedTreeCleanUpLevel >= 1 ) {
2329
2374
// A fiber was deleted from this parent fiber, but it's still part of
2330
2375
// the previous (alternate) parent fiber's list of children. Because
2331
2376
// children are a linked list, an earlier sibling that's still alive
0 commit comments