Skip to content

Commit 3abb4ca

Browse files
yungstersfabriziocucci
authored andcommitted
Animated: Defer onAnimatedValueUpdate on Attach + Native (#48715)
Summary: Pull Request resolved: #48715 {D68154908} fixed a problem with the `onAnimatedValueUpdate` listener not being correctly attached if `__attach` were called before `__makeNative` (which sets `__isNative` to true). We're potentially seeing production symptoms of stuttering interactions and user responsiveness, after queuing up many operations. Our hypothesis is that in scenarios where `ensureUpdateSubscriptionExists` is being called during `__makeNative` (instead of during `__attach`), a backup of operations occurs leading to these symptoms. This diff attempts to validate and mitigate this hypothesis by deferring `ensureUpdateSubscriptionExists` to when an `AnimatedValue` instance has had both `__attach` and `__makeNative` invoked. Changelog: [Internal] Differential Revision: D68236594 fbshipit-source-id: 2089100a773ebfc161fb5b567123eb58a893939f
1 parent bb5f971 commit 3abb4ca

File tree

1 file changed

+16
-8
lines changed

1 file changed

+16
-8
lines changed

packages/react-native/Libraries/Animated/nodes/AnimatedValue.js

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ function _executeAsAnimatedBatch(id: string, operation: () => void) {
8585
* See https://reactnative.dev/docs/animatedvalue
8686
*/
8787
export default class AnimatedValue extends AnimatedWithChildren {
88+
#attached: boolean = false;
8889
#updateSubscription: ?EventSubscription = null;
8990

9091
_value: number;
@@ -107,14 +108,8 @@ export default class AnimatedValue extends AnimatedWithChildren {
107108
}
108109

109110
__attach(): void {
110-
if (this.__isNative) {
111-
// NOTE: In theory, we should only need to call this when any listeners
112-
// are added. However, there is a global `onUserDrivenAnimationEnded`
113-
// listener that relies on `onAnimatedValueUpdate` having fired to update
114-
// the values in JavaScript. If that listener is removed, this could be
115-
// re-optimized.
116-
this.#ensureUpdateSubscriptionExists();
117-
}
111+
this.#attached = true;
112+
this.#ensureUpdateSubscriptionExists();
118113
}
119114

120115
__detach(): void {
@@ -126,6 +121,7 @@ export default class AnimatedValue extends AnimatedWithChildren {
126121
}
127122
this.stopAnimation();
128123
super.__detach();
124+
this.#attached = false;
129125
}
130126

131127
__getValue(): number {
@@ -137,10 +133,22 @@ export default class AnimatedValue extends AnimatedWithChildren {
137133
this.#ensureUpdateSubscriptionExists();
138134
}
139135

136+
/**
137+
* NOTE: In theory, we should only need to call this when any listeners
138+
* are added. However, there is a global `onUserDrivenAnimationEnded`
139+
* listener that relies on `onAnimatedValueUpdate` having fired to update
140+
* the values in JavaScript. If that listener is removed, this could be
141+
* re-optimized.
142+
*/
140143
#ensureUpdateSubscriptionExists(): void {
141144
if (this.#updateSubscription != null) {
142145
return;
143146
}
147+
// The order in which `__attach` and `__makeNative` are called is not
148+
// deterministic, and we only want to do this when both have occurred.
149+
if (!this.#attached || !this.__isNative) {
150+
return;
151+
}
144152
const nativeTag = this.__getNativeTag();
145153
NativeAnimatedAPI.startListeningToAnimatedNodeValue(nativeTag);
146154
const subscription: EventSubscription =

0 commit comments

Comments
 (0)