Skip to content

Commit 14b2e6b

Browse files
committed
Drain removable nodes on Reanimated commits
ReanimatedMountHook skipped handleNodeRemovals on Reanimated's own commits. While CSS animations run, every mount carries the mount trait, so the cleanup never ran for the duration of the animation. Tearing the view tree down mid-animation then leaked the unmounted nodes (and their still-scheduled CSS animations) in all update registries on Android. Always run handleNodeRemovals; gate only the commit-unpause on React commits.
1 parent 855bd45 commit 14b2e6b

1 file changed

Lines changed: 15 additions & 13 deletions

File tree

packages/react-native-reanimated/Common/cpp/reanimated/Fabric/ReanimatedMountHook.cpp

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ void ReanimatedMountHook::shadowTreeDidMount(
3030
auto reaShadowNode = std::reinterpret_pointer_cast<ReanimatedCommitShadowNode>(
3131
std::const_pointer_cast<RootShadowNode>(rootShadowNode));
3232

33+
// We mark reanimated commits with ReanimatedMountTrait. We don't want other
34+
// shadow nodes to use this trait, but since this rootShadowNode is Shared,
35+
// we don't have that guarantee. That's why we also unset this trait in the
36+
// commit hook. We remove it here mainly for the sake of cleanliness.
3337
const bool isReanimatedMount = reaShadowNode->hasReanimatedMountTrait();
3438
if (isReanimatedMount) {
35-
// We mark reanimated commits with ReanimatedMountTrait. We don't want other
36-
// shadow nodes to use this trait, but since this rootShadowNode is Shared,
37-
// we don't have that guarantee. That's why we also unset this trait in the
38-
// commit hook. We remove it here mainly for the sake of cleanliness.
3939
reaShadowNode->unsetReanimatedMountTrait();
4040
}
4141

@@ -44,17 +44,19 @@ void ReanimatedMountHook::shadowTreeDidMount(
4444
// Record the mounted tree for relative-length resolution.
4545
viewStylesRepository_->setLastMountedRoot(rootShadowNode);
4646

47-
if (isReanimatedMount) {
48-
return;
49-
}
50-
47+
// Always drain removable nodes, even on Reanimated's own commits. While CSS
48+
// animations run every mount carries the mount trait, so returning early here
49+
// would skip removals for the whole animation and leak unmounted nodes if the
50+
// tree is torn down mid-animation.
5151
updatesRegistryManager_->handleNodeRemovals(*rootShadowNode);
5252

53-
// When commit from React Native has finished, we reset the skip commit flag
54-
// in order to allow Reanimated to commit its tree
55-
updatesRegistryManager_->unpauseReanimatedCommits();
56-
if (updatesRegistryManager_->shouldCommitAfterPause()) {
57-
requestFlush_();
53+
if (!isReanimatedMount) {
54+
// When a commit from React Native has finished, we reset the skip commit
55+
// flag in order to allow Reanimated to commit its tree.
56+
updatesRegistryManager_->unpauseReanimatedCommits();
57+
if (updatesRegistryManager_->shouldCommitAfterPause()) {
58+
requestFlush_();
59+
}
5860
}
5961
}
6062
}

0 commit comments

Comments
 (0)