Skip to content

Commit 29ef3ed

Browse files
committed
chore: add harness test for dispose during Fabric prop diffing
Rapidly switches RiveView file prop to test that callDispose doesn't crash React Fabric's prop diffing. Verifies the fix from #227.
1 parent 5822b67 commit 29ef3ed

2 files changed

Lines changed: 2118 additions & 38 deletions

File tree

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import {
2+
describe,
3+
it,
4+
expect,
5+
render,
6+
cleanup,
7+
} from 'react-native-harness';
8+
import { useState, useEffect } from 'react';
9+
import { View, Text } from 'react-native';
10+
import {
11+
RiveView,
12+
useRiveFile,
13+
Fit,
14+
} from '@rive-app/react-native';
15+
16+
const BOUNCING_BALL = require('../assets/rive/bouncing_ball.riv');
17+
const COUNTER = require('../assets/rive/counter.riv');
18+
const RATING = require('../assets/rive/rating.riv');
19+
20+
const FILES = [BOUNCING_BALL, COUNTER, RATING];
21+
22+
function RiveBox({ source }: { source: number }) {
23+
const { riveFile } = useRiveFile(source);
24+
25+
return (
26+
<View style={{ width: 100, height: 100 }}>
27+
{riveFile && (
28+
<RiveView file={riveFile} autoPlay fit={Fit.Contain} style={{ flex: 1 }} />
29+
)}
30+
</View>
31+
);
32+
}
33+
34+
function FileSwitcher({
35+
fileIdx,
36+
}: {
37+
fileIdx: number;
38+
}) {
39+
const source = FILES[fileIdx % FILES.length]!;
40+
return <RiveBox source={source} />;
41+
}
42+
43+
describe('dispose during Fabric prop diffing', () => {
44+
it('rapidly switching file prop should not crash', async () => {
45+
let setIdx: (fn: (i: number) => number) => void;
46+
47+
function Wrapper() {
48+
const [fileIdx, _setIdx] = useState(0);
49+
setIdx = _setIdx;
50+
return <FileSwitcher fileIdx={fileIdx} />;
51+
}
52+
53+
await render(<Wrapper />);
54+
55+
// Wait for initial file to load
56+
await new Promise((r) => setTimeout(r, 500));
57+
58+
// Rapid 3x switch — same pattern that crashes on RN 0.83
59+
setIdx!((i) => i + 1);
60+
await new Promise((r) => setTimeout(r, 50));
61+
setIdx!((i) => i + 1);
62+
await new Promise((r) => setTimeout(r, 50));
63+
setIdx!((i) => i + 1);
64+
65+
// Wait for everything to settle
66+
await new Promise((r) => setTimeout(r, 500));
67+
68+
// If we get here without crashing, the test passes
69+
expect(true).toBe(true);
70+
71+
cleanup();
72+
});
73+
74+
it('switching file 10 times rapidly should not crash', async () => {
75+
let setIdx: (fn: (i: number) => number) => void;
76+
77+
function Wrapper() {
78+
const [fileIdx, _setIdx] = useState(0);
79+
setIdx = _setIdx;
80+
return <FileSwitcher fileIdx={fileIdx} />;
81+
}
82+
83+
await render(<Wrapper />);
84+
await new Promise((r) => setTimeout(r, 500));
85+
86+
for (let i = 0; i < 10; i++) {
87+
setIdx!((j) => j + 1);
88+
await new Promise((r) => setTimeout(r, 30));
89+
}
90+
91+
await new Promise((r) => setTimeout(r, 500));
92+
expect(true).toBe(true);
93+
94+
cleanup();
95+
});
96+
});

0 commit comments

Comments
 (0)