Skip to content

Commit 157cdd7

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 013fe01 commit 157cdd7

1 file changed

Lines changed: 18 additions & 11 deletions

File tree

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

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,31 @@ void ReanimatedMountHook::shadowTreeDidMount(
2626
auto reaShadowNode = std::reinterpret_pointer_cast<ReanimatedCommitShadowNode>(
2727
std::const_pointer_cast<RootShadowNode>(rootShadowNode));
2828

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

3838
{
3939
auto lock = updatesRegistryManager_->lock();
40+
41+
// Always drain removable nodes, even on Reanimated's own commits. While CSS
42+
// animations run every mount carries the mount trait, so returning early here
43+
// would skip removals for the whole animation and leak unmounted nodes if the
44+
// tree is torn down mid-animation.
4045
updatesRegistryManager_->handleNodeRemovals(*rootShadowNode);
4146

42-
// When commit from React Native has finished, we reset the skip commit flag
43-
// in order to allow Reanimated to commit its tree
44-
updatesRegistryManager_->unpauseReanimatedCommits();
45-
if (updatesRegistryManager_->shouldCommitAfterPause()) {
46-
requestFlush_();
47+
if (!isReanimatedMount) {
48+
// When a commit from React Native has finished, we reset the skip commit
49+
// flag in order to allow Reanimated to commit its tree.
50+
updatesRegistryManager_->unpauseReanimatedCommits();
51+
if (updatesRegistryManager_->shouldCommitAfterPause()) {
52+
requestFlush_();
53+
}
4754
}
4855
}
4956
}

0 commit comments

Comments
 (0)