Skip to content

Commit b49e37f

Browse files
tomekzawclaude
andauthored
fix: re-add LightNode before starting exiting animation (#9646)
## Summary Fixes #9615 — the `[LA] Reduced Motion` "always reduce" example crashes on iOS when the box is hidden. In `startAnimationsRecursively`, the exiting node was re-added to `lightNodes_` *after* `startExitingAnimation`. On iOS, `IOSUIScheduler::scheduleOnUI` runs the job synchronously when already on the main thread, and Fabric mounting runs on the main thread — so `startExitingAnimation` can re-enter `endLayoutAnimation` before control returns. With `ReduceMotion.Always` the exiting animation finishes within the same frame, so the re-entrant `endLayoutAnimation(tag, /*shouldRemove=*/true)` runs while the node is still missing from `lightNodes_` (it was erased by the `Delete` mutation and not yet re-added). `lightNodes_[tag]` then inserts and dereferences a null `shared_ptr`, crashing. This is iOS-only because Android queues the job instead of running it inline, so the re-add happens first. The fix re-adds the node to `lightNodes_` *before* calling `startExitingAnimation`, keeping the light tree consistent for the (possibly re-entrant) `endLayoutAnimation` call. ## Test plan 1. Build the `fabric-example` app on iOS. 2. Open the `[LA] Reduced motion` example. 3. In the `always reduce` section, toggle the box (`show` / `hide`) several times. 4. The box animates instantly and the app no longer crashes. Verified on the iOS simulator: before the fix, the first `hide` reliably crashes; after the fix, repeated show/hide cycles run without crashing and the box hides correctly. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent f96e497 commit b49e37f

1 file changed

Lines changed: 1 addition & 1 deletion

File tree

packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Experimental.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,8 +516,8 @@ bool LayoutAnimationsProxy_Experimental::startAnimationsRecursively(
516516

517517
if (hasExitAnimation) {
518518
node->state = ANIMATING;
519-
startExitingAnimation(node);
520519
lightNodes_[node->current.tag] = node;
520+
startExitingAnimation(node);
521521
} else {
522522
layoutAnimationsManager_->clearLayoutAnimationConfig(node->current.tag);
523523
}

0 commit comments

Comments
 (0)