From b3fe06b2685e526fe2bc420a902a3ccd4e1381df Mon Sep 17 00:00:00 2001 From: Tim Yung Date: Wed, 2 Oct 2024 02:07:00 -0700 Subject: [PATCH] Animated: Change `onEnd` to Private Property (#46721) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/46721 Currently, the `Animation` class (from Animated) defines an unofficially protected `__onEnd` property that subclasses are expected to populate. However, subclasses are expected to *not* invoke `__onEnd`, but instead call `__debouncedOnEnd`. In order to make this code less fragile, this commit refactors the `Animation` class and its subclasses so that `onEnd` is stored in a private property and initialized by the `start` method in the `Animation` parent class. Now, the only way to invoke the `onEnd` callback is by calling `__debouncedOnEnd`. The subclasses have been adjusted to call the `start` superclass method to initialize this. Changelog: [General][Changed] - The `Animation` superclass no longer exposes `__onEnd` as a property. Subclasses must instead invoke `super.start(…)` in their `start()` implementation. Reviewed By: javache Differential Revision: D63572063 fbshipit-source-id: 221153ca890c67c59713d607032536f57d7a5b0a --- .../Libraries/Animated/animations/Animation.js | 11 +++++++---- .../Libraries/Animated/animations/DecayAnimation.js | 3 ++- .../Libraries/Animated/animations/SpringAnimation.js | 3 ++- .../Libraries/Animated/animations/TimingAnimation.js | 3 ++- .../__tests__/__snapshots__/public-api-test.js.snap | 1 - 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/react-native/Libraries/Animated/animations/Animation.js b/packages/react-native/Libraries/Animated/animations/Animation.js index 8152fa30bcc..37d848195ad 100644 --- a/packages/react-native/Libraries/Animated/animations/Animation.js +++ b/packages/react-native/Libraries/Animated/animations/Animation.js @@ -36,9 +36,10 @@ let startNativeAnimationNextId = 1; // Once an animation has been stopped or finished its course, it will // not be reused. export default class Animation { + #onEnd: ?EndCallback; + __active: boolean; __isInteraction: boolean; - __onEnd: ?EndCallback; __iterations: number; __isLooping: ?boolean; @@ -50,7 +51,9 @@ export default class Animation { onEnd: ?EndCallback, previousAnimation: ?Animation, animatedValue: AnimatedValue, - ): void {} + ): void { + this.#onEnd = onEnd; + } stop(): void { if (this._nativeId) { @@ -66,8 +69,8 @@ export default class Animation { // Helper function for subclasses to make sure onEnd is only called once. __debouncedOnEnd(result: EndResult): void { - const onEnd = this.__onEnd; - this.__onEnd = null; + const onEnd = this.#onEnd; + this.#onEnd = null; onEnd && onEnd(result); } diff --git a/packages/react-native/Libraries/Animated/animations/DecayAnimation.js b/packages/react-native/Libraries/Animated/animations/DecayAnimation.js index 915d913b6d8..0518942633e 100644 --- a/packages/react-native/Libraries/Animated/animations/DecayAnimation.js +++ b/packages/react-native/Libraries/Animated/animations/DecayAnimation.js @@ -79,11 +79,12 @@ export default class DecayAnimation extends Animation { previousAnimation: ?Animation, animatedValue: AnimatedValue, ): void { + super.start(fromValue, onUpdate, onEnd, previousAnimation, animatedValue); + this.__active = true; this._lastValue = fromValue; this._fromValue = fromValue; this._onUpdate = onUpdate; - this.__onEnd = onEnd; this._startTime = Date.now(); if (!this._useNativeDriver && animatedValue.__isNative === true) { diff --git a/packages/react-native/Libraries/Animated/animations/SpringAnimation.js b/packages/react-native/Libraries/Animated/animations/SpringAnimation.js index d5641ad95a2..6b7ab0c8c8c 100644 --- a/packages/react-native/Libraries/Animated/animations/SpringAnimation.js +++ b/packages/react-native/Libraries/Animated/animations/SpringAnimation.js @@ -202,12 +202,13 @@ export default class SpringAnimation extends Animation { previousAnimation: ?Animation, animatedValue: AnimatedValue, ): void { + super.start(fromValue, onUpdate, onEnd, previousAnimation, animatedValue); + this.__active = true; this._startPosition = fromValue; this._lastPosition = this._startPosition; this._onUpdate = onUpdate; - this.__onEnd = onEnd; this._lastTime = Date.now(); this._frameTime = 0.0; diff --git a/packages/react-native/Libraries/Animated/animations/TimingAnimation.js b/packages/react-native/Libraries/Animated/animations/TimingAnimation.js index c4436f25ffc..f287c0dca75 100644 --- a/packages/react-native/Libraries/Animated/animations/TimingAnimation.js +++ b/packages/react-native/Libraries/Animated/animations/TimingAnimation.js @@ -107,10 +107,11 @@ export default class TimingAnimation extends Animation { previousAnimation: ?Animation, animatedValue: AnimatedValue, ): void { + super.start(fromValue, onUpdate, onEnd, previousAnimation, animatedValue); + this.__active = true; this._fromValue = fromValue; this._onUpdate = onUpdate; - this.__onEnd = onEnd; const start = () => { if (!this._useNativeDriver && animatedValue.__isNative === true) { diff --git a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap index b73dfce2717..79c25d600ca 100644 --- a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap +++ b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap @@ -427,7 +427,6 @@ export type AnimationConfig = { declare export default class Animation { __active: boolean; __isInteraction: boolean; - __onEnd: ?EndCallback; __iterations: number; __isLooping: ?boolean; _nativeId: number;