Skip to content

Commit 2a7691b

Browse files
committed
feat: add useRiveFile hook
1 parent 204a0ea commit 2a7691b

File tree

8 files changed

+212
-289
lines changed

8 files changed

+212
-289
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,15 @@ The following runtime features are currently supported:
9292
| Animation selection || Animation playback not planned, use state machines |
9393
| View autoPlay & play/pause || Control view playback |
9494
| Fit & Alignment || Fit modes supported, alignment coming soon |
95-
| Layout & Responsiveness | ⚠️ | Basic responsive layouts supported |
95+
| Layout & Responsiveness | | Basic responsive layouts supported |
9696
| Data Binding | ⚠️ | Control data binding through runtime code |
9797
| Asset management | 🚧 | Out-of-band assets not yet supported |
9898
| State machine inputs || Get/Set (nested) state machine inputs (legacy, see data binding) |
9999
| Text Runs || Update (nested) text runs (legacy, see data binding) |
100100
| Rive Events || Listen to Rive events |
101101
| Rive Audio || Full Rive audio playback supported |
102102
| `useRive()` hook || Convenient hook to access the Rive View ref after load |
103-
| `useRiveFile()` hook | 🚧 | Convenient hook to load a Rive file |
103+
| `useRiveFile()` hook | | Convenient hook to load a Rive file |
104104
| `RiveView` error handling | 🚧 | Error handler for failed view operations |
105105
| `source` .riv file loading || Conveniently load .riv files from JS source |
106106
| Renderer options || Single renderer option available (Rive) |

example/src/pages/RiveDataBindingExample.tsx

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,61 +3,66 @@ import { useState, useEffect } from 'react';
33
import {
44
Fit,
55
RiveView,
6-
RiveFileFactory,
76
useRive,
87
useRiveNumber,
9-
type RiveFile,
108
type ViewModelInstance,
119
useRiveString,
1210
useRiveColor,
1311
useRiveTrigger,
12+
useRiveFile,
1413
} from 'react-native-rive';
1514

1615
export default function DataBindingExample() {
1716
const { riveViewRef, setHybridRef } = useRive();
18-
const [riveFile, setRiveFile] = useState<RiveFile | null>(null);
1917
const [viewModelInstance, setViewModelInstance] =
2018
useState<ViewModelInstance | null>(null);
21-
const [isLoading, setIsLoading] = useState(true);
22-
const [error, setError] = useState<string | null>(null);
19+
const [viewModelError, setViewModelError] = useState<string | null>(null);
2320

24-
// Load Rive file immediately
21+
const { riveFile, isLoading, error } = useRiveFile(
22+
require('../../assets/rive/rewards_source.riv')
23+
);
24+
25+
// Create view model instance when Rive file is loaded
2526
useEffect(() => {
26-
const initializeRiveFileAndData = async () => {
27+
if (riveFile) {
2728
try {
28-
const file = await RiveFileFactory.fromSource(
29-
require('../../assets/rive/rewards_source.riv')
30-
);
31-
setRiveFile(file);
32-
const viewModel = file.defaultArtboardViewModel();
33-
setViewModelInstance(viewModel?.createDefaultInstance() ?? null);
34-
setIsLoading(false);
35-
setError(null);
29+
const viewModel = riveFile.defaultArtboardViewModel();
30+
if (!viewModel) {
31+
throw new Error('No default artboard view model found');
32+
}
33+
const instance = viewModel.createDefaultInstance();
34+
if (!instance) {
35+
throw new Error('Failed to create view model instance');
36+
}
37+
setViewModelInstance(instance);
38+
setViewModelError(null);
3639
} catch (err) {
37-
setError(
38-
err instanceof Error ? err.message : 'Failed to load Rive file'
40+
setViewModelError(
41+
err instanceof Error
42+
? err.message
43+
: 'Failed to create view model instance'
3944
);
40-
setIsLoading(false);
45+
setViewModelInstance(null);
4146
}
42-
};
43-
44-
initializeRiveFileAndData();
45-
}, []);
47+
}
48+
}, [riveFile]);
4649

47-
// Release the Rive file when the component unmounts
50+
// Cleanup view model instance when component unmounts or the view model instance changes
4851
useEffect(() => {
4952
return () => {
50-
if (riveFile) {
51-
riveFile.release(); // IMPORTANT: This ensures that the native resources are deleted
52-
}
53+
viewModelInstance?.dispose();
5354
};
54-
}, [riveFile]);
55+
}, [viewModelInstance]);
5556

5657
// Bind the view model instance to the RiveView
5758
useEffect(() => {
5859
if (viewModelInstance && riveViewRef) {
59-
console.log('Binding the instance');
60-
riveViewRef?.bindViewModelInstance(viewModelInstance);
60+
try {
61+
console.log('Binding the instance');
62+
riveViewRef.bindViewModelInstance(viewModelInstance);
63+
} catch (err) {
64+
console.error('Failed to bind view model instance:', err);
65+
}
6166
}
6267
}, [viewModelInstance, riveViewRef]);
6368

@@ -67,7 +72,9 @@ export default function DataBindingExample() {
6772
viewModelInstance
6873
);
6974
useEffect(() => {
70-
console.log('coinValue', coinValue);
75+
if (coinValue !== undefined) {
76+
console.log('coinValue', coinValue);
77+
}
7178
}, [coinValue]);
7279

7380
if (coinValueError) {
@@ -118,17 +125,20 @@ export default function DataBindingExample() {
118125
// Set the initial values of the properties
119126
useEffect(() => {
120127
if (viewModelInstance) {
121-
setButtonText("Let's go!");
122-
setBarColor('#0000FF');
123-
// setBarColor({ r: 0, g: 255, b: 0, a: 255 });
128+
try {
129+
setButtonText("Let's go!");
130+
setBarColor('#0000FF');
131+
} catch (err) {
132+
console.error('Failed to set initial values:', err);
133+
}
124134
}
125135
}, [setBarColor, setButtonText, viewModelInstance]);
126136

127137
return (
128138
<View style={styles.container}>
129139
<View style={styles.riveContainer}>
130-
{error ? (
131-
<Text style={styles.errorText}>{error}</Text>
140+
{error || viewModelError ? (
141+
<Text style={styles.errorText}>{error || viewModelError}</Text>
132142
) : isLoading ? (
133143
<ActivityIndicator size="large" color="#0000ff" />
134144
) : (

example/src/pages/RiveEventsExample.tsx

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,50 +3,19 @@ import { useState, useEffect } from 'react';
33
import {
44
Fit,
55
RiveView,
6-
RiveFileFactory,
76
useRive,
8-
type RiveFile,
9-
RiveEventType,
7+
useRiveFile,
108
type RiveEvent,
9+
RiveEventType,
1110
} from 'react-native-rive';
1211

1312
export default function EventsExample() {
1413
const { riveViewRef, setHybridRef } = useRive();
15-
const [riveFile, setRiveFile] = useState<RiveFile | null>(null);
16-
const [isLoading, setIsLoading] = useState(true);
17-
const [error, setError] = useState<string | null>(null);
14+
const { riveFile, isLoading, error } = useRiveFile(
15+
require('../../assets/rive/rating.riv')
16+
);
1817
const [lastEvent, setLastEvent] = useState<RiveEvent | null>(null);
1918

20-
useEffect(() => {
21-
let currentFile: RiveFile | null = null;
22-
23-
const loadRiveFile = async () => {
24-
try {
25-
const file = await RiveFileFactory.fromSource(
26-
require('../../assets/rive/rating.riv')
27-
);
28-
currentFile = file;
29-
setRiveFile(file);
30-
setIsLoading(false);
31-
setError(null);
32-
} catch (err) {
33-
setError(
34-
err instanceof Error ? err.message : 'Failed to load Rive file'
35-
);
36-
setIsLoading(false);
37-
}
38-
};
39-
40-
loadRiveFile();
41-
42-
// Cleanup function to release the Rive file when component unmounts
43-
return () => {
44-
if (currentFile) {
45-
currentFile.release();
46-
}
47-
};
48-
}, []);
49-
5019
const handleRiveEvent = (event: any) => {
5120
console.log('Rive Event:', event);
5221
if (event.type === RiveEventType.General) {

0 commit comments

Comments
 (0)