Skip to content

Commit 7cc257c

Browse files
committed
fix: Animated.loop and Animated.sequence crash if resetBeforeIteration is false, original author asyler (from PR 44031 on RN repo)
1 parent 922c134 commit 7cc257c

File tree

2 files changed

+73
-0
lines changed

2 files changed

+73
-0
lines changed

packages/react-native-web/src/exports/Animated/__tests__/index-test.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import Animated from '..';
1111
import Easing from '../../Easing';
12+
import AnimatedImplementation from '../../../vendor/react-native/Animated/AnimatedImplementation';
1213

1314
const AnimatedInterpolation = Animated.Interpolation;
1415

@@ -329,4 +330,74 @@ describe('Animated', () => {
329330
expect(interpolation(2 / 3)).toBe('rgba(0, 0, 0, 0.667)');
330331
});
331332
});
333+
334+
describe('sequence and loop', () => {
335+
it('supports restarting sequence after it was stopped during execution', () => {
336+
const anim1 = { start: jest.fn(), stop: jest.fn() };
337+
const anim2 = { start: jest.fn(), stop: jest.fn() };
338+
const cb = jest.fn();
339+
340+
const seq = AnimatedImplementation.sequence([anim1, anim2]);
341+
342+
seq.start(cb);
343+
344+
anim1.start.mock.calls[0][0]({ finished: true });
345+
seq.stop();
346+
347+
// anim1 should be finished so anim2 should also start
348+
expect(anim1.start).toHaveBeenCalledTimes(1);
349+
expect(anim2.start).toHaveBeenCalledTimes(1);
350+
351+
seq.start(cb);
352+
353+
// after restart the sequence should resume from the anim2
354+
expect(anim1.start).toHaveBeenCalledTimes(1);
355+
expect(anim2.start).toHaveBeenCalledTimes(2);
356+
});
357+
358+
it('supports restarting sequence after it was finished without a reset', () => {
359+
const anim1 = { start: jest.fn(), stop: jest.fn() };
360+
const anim2 = { start: jest.fn(), stop: jest.fn() };
361+
const cb = jest.fn();
362+
363+
const seq = AnimatedImplementation.sequence([anim1, anim2]);
364+
365+
seq.start(cb);
366+
anim1.start.mock.calls[0][0]({ finished: true });
367+
anim2.start.mock.calls[0][0]({ finished: true });
368+
369+
// sequence should be finished
370+
expect(cb).toBeCalledWith({ finished: true });
371+
372+
seq.start(cb);
373+
374+
// sequence should successfully restart from the anim1
375+
expect(anim1.start).toHaveBeenCalledTimes(2);
376+
expect(anim2.start).toHaveBeenCalledTimes(1);
377+
});
378+
379+
it('restarts sequence normally in a loop if resetBeforeIteration is false', () => {
380+
const anim1 = { start: jest.fn(), stop: jest.fn() };
381+
const anim2 = { start: jest.fn(), stop: jest.fn() };
382+
const seq = AnimatedImplementation.sequence([anim1, anim2]);
383+
384+
const loop = AnimatedImplementation.loop(seq, {
385+
resetBeforeIteration: false
386+
});
387+
388+
loop.start();
389+
390+
expect(anim1.start).toHaveBeenCalledTimes(1);
391+
392+
anim1.start.mock.calls[0][0]({ finished: true });
393+
394+
expect(anim2.start).toHaveBeenCalledTimes(1);
395+
396+
anim2.start.mock.calls[0][0]({ finished: true });
397+
398+
// after anim2 is finished, the sequence is finished,
399+
// hence the loop iteration is finished, so the next iteration starts
400+
expect(anim1.start).toHaveBeenCalledTimes(2);
401+
});
402+
});
332403
});

packages/react-native-web/src/vendor/react-native/Animated/AnimatedImplementation.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,8 @@ const sequence = function (
318318
current++;
319319

320320
if (current === animations.length) {
321+
// if the start is called, even without a reset, it should start from the beginning
322+
current = 0;
321323
callback && callback(result);
322324
return;
323325
}

0 commit comments

Comments
 (0)