Repository navigation
Fix Animated values going stale across Activity hide and reveal - #59716
Open
Hassan1305 wants to merge 1 commit into
Open
Hassan1305 wants to merge 1 commit into
Hassan1305 wants to merge 1 commit into
Conversation
A stopped native animation reports its value asynchronously, which overwrote the value assigned by resetAnimation() or setValue(). TouchableOpacity resets its value in componentWillUnmount, which also runs when an Activity hides it, so it came back at activeOpacity. Updates are also dropped while hidden because the ref is detached, so a component that does not otherwise re-render on reveal kept its last committed value. Schedule an update when revealed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary:
Fixes #58800. A
TouchableOpacitywhoseonPresshides its parent<Activity>comes back stuck atactiveOpacity. There are two causes, both inAnimated:Activityhiding runscomponentWillUnmount, whereTouchableOpacitycallsanim.resetAnimation()while the press-out animation is running. JS sets the value to 1, then native's asynchronous end result for the stopped animation (value: 0.2) overwrites it. JS now disagrees with native, and the next render commits 0.2.setValue()has the same race, with or withoutActivity. A stopped animation's end result no longer syncs the value afterresetAnimation()orsetValue().onUpdateRef, so an animation finishing while hidden does not schedule a render. A component that does not otherwise re-render on reveal (for example behindmemo) keeps its last committed value.useAnimatedPropsnow schedules an update when revealed, using a layout effect because those only re-run for the same node on reveal. A ref callback would not work: inline refs re-run it on every render, which would loop.Trace on iOS before the fix, from temporary logging of rendered opacity:
Animated.View:r=0.2 HIDE ... pout:{"value":1,"finished":true} SHOW plainJS=1 r=1. It recovers when its parent re-renders it.TouchableOpacity:r=0.2 HIDE ... SHOW r=0.2. The JS value itself is stale.Changelog:
[GENERAL] [FIXED] - Fix
Animatedvalues going stale when an animation is stopped bysetValue/resetAnimationor ends while hidden byActivity, which leftTouchableOpacitystuck atactiveOpacityTest Plan:
New Jest tests fail without the fix and pass with it:
does not let a stopped animation overwrite the value set by resetAnimation(without fix: expected 1, received 0.2)does not let a stopped animation overwrite the value set by setValue(without fix: expected 0.5, received 0.2)commits the latest value when revealed after animating while hidden(without fix: expected 1, received 0.2)RNTester, iOS Simulator (iPhone 17 Pro, iOS 26.2), on
main, using a temporary Playground withTouchableOpacitybuttons that hide their ownActivity, then show it again:Before: both the plain and the
memo-wrappedTouchableOpacitycome back at about 0.2. After: both come back at full opacity.RNTester, Android emulator (Medium Phone, API 36.1), same screen: the bug did not reproduce without the fix (hide confirmed, unfixed bundle confirmed), and buttons also come back at full opacity with it, so no regression there. I did not investigate why Android differs.
JavaScript CI, all passing:
Not covered:
cxxNativeAnimatedEnabled(the OSS canary backend, forced on in Fantom): amemo-wrappedAnimated.Viewthat finishes animating while hidden still reveals with the old opacity in Fantom. There, native writes the animated value to the shadow tree and the JS re-render does not change props. That is a separate C++ path and this PR does not change it, so no Fantom test is included. Fantom cannot drive native animations with the flag off.🤖 Generated with Claude Code
