Skip to content

Commit 2006ca5

Browse files
committed
feat: add useViewModelInstance hook with ViewModel and RiveViewRef support
1 parent 3a86efa commit 2006ca5

3 files changed

Lines changed: 96 additions & 8 deletions

File tree

example/src/pages/MenuListExample.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
type RiveFile,
1616
useRiveFile,
1717
useRiveList,
18+
useViewModelInstance,
1819
} from '@rive-app/react-native';
1920
import { type Metadata } from '../helpers/metadata';
2021

@@ -38,17 +39,12 @@ export default function MenuListExample() {
3839

3940
function WithViewModelSetup({ file }: { file: RiveFile }) {
4041
const viewModel = useMemo(() => file.viewModelByName('main'), [file]);
41-
const instance = useMemo(
42-
() => viewModel?.createDefaultInstance(),
43-
[viewModel]
44-
);
42+
const instance = useViewModelInstance(viewModel);
4543

46-
if (!instance || !viewModel) {
44+
if (!instance) {
4745
return (
4846
<Text style={styles.errorText}>
49-
{!viewModel
50-
? "No view model 'main' found"
51-
: 'Failed to create view model instance'}
47+
Failed to create view model instance for 'main'
5248
</Text>
5349
);
5450
}

src/hooks/useViewModelInstance.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { useState, useEffect } from 'react';
2+
import type { ViewModel, ViewModelInstance } from '../specs/ViewModel.nitro';
3+
import type { RiveViewRef } from '../index';
4+
5+
export interface UseViewModelInstanceParams {
6+
/**
7+
* Get a specifically named instance from the ViewModel.
8+
*/
9+
name?: string;
10+
/**
11+
* Create a new (blank) instance from the ViewModel.
12+
*/
13+
useNew?: boolean;
14+
}
15+
16+
type ViewModelSource = ViewModel | RiveViewRef | null | undefined;
17+
18+
function isRiveViewRef(source: ViewModelSource): source is RiveViewRef {
19+
return (
20+
source !== null && source !== undefined && 'getViewModelInstance' in source
21+
);
22+
}
23+
24+
/**
25+
* Hook for getting a ViewModelInstance from a ViewModel or RiveViewRef.
26+
*
27+
* @param source - The ViewModel or RiveViewRef to get an instance from
28+
* @param params - Configuration for which instance to retrieve (only used with ViewModel)
29+
* @returns The ViewModelInstance or null if not found
30+
*
31+
* @example
32+
* ```tsx
33+
* // From RiveViewRef (get auto-bound instance)
34+
* const { riveViewRef, setHybridRef } = useRive();
35+
* const instance = useViewModelInstance(riveViewRef);
36+
* ```
37+
*
38+
* @example
39+
* ```tsx
40+
* // From ViewModel
41+
* const viewModel = file.viewModelByName('main');
42+
* const instance = useViewModelInstance(viewModel);
43+
* ```
44+
*
45+
* @example
46+
* ```tsx
47+
* // Create a new blank instance from ViewModel
48+
* const viewModel = file.viewModelByName('TodoItem');
49+
* const newInstance = useViewModelInstance(viewModel, { useNew: true });
50+
* ```
51+
*/
52+
export function useViewModelInstance(
53+
source: ViewModelSource,
54+
params?: UseViewModelInstanceParams
55+
): ViewModelInstance | null {
56+
const [instance, setInstance] = useState<ViewModelInstance | null>(null);
57+
58+
const name = params?.name;
59+
const useNew = params?.useNew ?? false;
60+
61+
useEffect(() => {
62+
if (!source) {
63+
setInstance(null);
64+
return;
65+
}
66+
67+
if (isRiveViewRef(source)) {
68+
const vmi = source.getViewModelInstance();
69+
setInstance(vmi ?? null);
70+
return;
71+
}
72+
73+
let vmi: ViewModelInstance | undefined;
74+
if (name) {
75+
vmi = source.createInstanceByName(name);
76+
} else if (useNew) {
77+
vmi = source.createInstance();
78+
} else {
79+
vmi = source.createDefaultInstance();
80+
}
81+
setInstance(vmi ?? null);
82+
83+
return () => {
84+
if (vmi) {
85+
vmi.dispose();
86+
}
87+
};
88+
}, [source, name, useNew]);
89+
90+
return instance;
91+
}

src/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export { useRiveEnum } from './hooks/useRiveEnum';
4949
export { useRiveColor } from './hooks/useRiveColor';
5050
export { useRiveTrigger } from './hooks/useRiveTrigger';
5151
export { useRiveList } from './hooks/useRiveList';
52+
export { useViewModelInstance } from './hooks/useViewModelInstance';
5253
export { useRiveFile } from './hooks/useRiveFile';
5354
export { type RiveFileInput } from './hooks/useRiveFile';
5455
export { DataBindMode };

0 commit comments

Comments
 (0)