diff --git a/packages/react-native/Libraries/Animated/AnimatedImplementation.js b/packages/react-native/Libraries/Animated/AnimatedImplementation.js index cdff0a304807..88152fa7565e 100644 --- a/packages/react-native/Libraries/Animated/AnimatedImplementation.js +++ b/packages/react-native/Libraries/Animated/AnimatedImplementation.js @@ -42,7 +42,7 @@ export type CompositeAnimation = { start: (callback?: ?EndCallback, isLooping?: boolean) => void, stop: () => void, reset: () => void, - _startNativeLoop: (iterations?: number) => void, + _startNativeLoop: (iterations: number, callback: ?EndCallback) => void, _isUsingNativeDriver: () => boolean, ... }; @@ -192,9 +192,9 @@ const springImpl = function ( value.resetAnimation(); }, - _startNativeLoop: function (iterations?: number): void { + _startNativeLoop(iterations: number, callback: ?EndCallback): void { const singleConfig = {...config, iterations}; - start(value, singleConfig); + start(value, singleConfig, callback); }, _isUsingNativeDriver: function (): boolean { @@ -246,9 +246,9 @@ const timingImpl = function ( value.resetAnimation(); }, - _startNativeLoop: function (iterations?: number): void { + _startNativeLoop(iterations: number, callback: ?EndCallback): void { const singleConfig = {...config, iterations}; - start(value, singleConfig); + start(value, singleConfig, callback); }, _isUsingNativeDriver: function (): boolean { @@ -288,9 +288,9 @@ const decayImpl = function ( value.resetAnimation(); }, - _startNativeLoop: function (iterations?: number): void { + _startNativeLoop(iterations: number, callback: ?EndCallback): void { const singleConfig = {...config, iterations}; - start(value, singleConfig); + start(value, singleConfig, callback); }, _isUsingNativeDriver: function (): boolean { @@ -484,7 +484,7 @@ const loopImpl = function ( callback && callback({finished: true}); } else { if (animation._isUsingNativeDriver()) { - animation._startNativeLoop(iterations); + animation._startNativeLoop(iterations, callback); } else { restart(); // Start looping recursively on the js thread } diff --git a/packages/react-native/Libraries/Animated/AnimatedMock.js b/packages/react-native/Libraries/Animated/AnimatedMock.js index 87338979f966..0257dc687dd1 100644 --- a/packages/react-native/Libraries/Animated/AnimatedMock.js +++ b/packages/react-native/Libraries/Animated/AnimatedMock.js @@ -65,7 +65,7 @@ export type CompositeAnimation = { start: (callback?: ?EndCallback) => void, stop: () => void, reset: () => void, - _startNativeLoop: (iterations?: number) => void, + _startNativeLoop: (iterations: number, callback: ?EndCallback) => void, _isUsingNativeDriver: () => boolean, ... }; diff --git a/packages/react-native/Libraries/Animated/__tests__/Animated-test.js b/packages/react-native/Libraries/Animated/__tests__/Animated-test.js index 53cb752e58c2..73f9d5e81a76 100644 --- a/packages/react-native/Libraries/Animated/__tests__/Animated-test.js +++ b/packages/react-native/Libraries/Animated/__tests__/Animated-test.js @@ -683,6 +683,31 @@ describe('Animated', () => { expect(animation.reset).toHaveBeenCalledTimes(1); expect(cb).toBeCalledWith({finished: false}); }); + + it('stops looping native animations', () => { + const value = new Animated.Value(0); + const animation = Animated.timing(value, { + toValue: 1, + useNativeDriver: true, + }); + + jest.spyOn(animation, '_startNativeLoop'); + jest.spyOn(animation, 'stop'); + + const callback = jest.fn(); + + const loop = Animated.loop(animation); + loop.start(callback); + + expect(animation._startNativeLoop).toBeCalledTimes(1); + expect(animation.stop).not.toBeCalled(); + expect(callback).not.toBeCalled(); + + loop.stop(); + + expect(animation.stop).toBeCalled(); + expect(callback).toBeCalledWith({finished: false}); + }); }); it('does not reset animation in a loop if resetBeforeIteration is false', () => {