Skip to content

Commit 7ba48ca

Browse files
committed
migrate examples to async API; add @typescript-eslint/no-deprecated lint rule
1 parent 90ea02f commit 7ba48ca

14 files changed

+97
-88
lines changed

eslint.config.mjs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import { fixupConfigRules } from '@eslint/compat';
1+
import { fixupConfigRules, fixupPluginRules } from '@eslint/compat';
22
import { FlatCompat } from '@eslint/eslintrc';
33
import js from '@eslint/js';
4+
import tsPlugin from '@typescript-eslint/eslint-plugin';
5+
import tsParser from '@typescript-eslint/parser';
46
import prettier from 'eslint-plugin-prettier';
57
import { defineConfig } from 'eslint/config';
68
import path from 'node:path';
@@ -15,8 +17,8 @@ const compat = new FlatCompat({
1517
});
1618

1719
export default defineConfig([
20+
...fixupConfigRules(compat.extends('@react-native', 'prettier')),
1821
{
19-
extends: fixupConfigRules(compat.extends('@react-native', 'prettier')),
2022
plugins: { prettier },
2123
rules: {
2224
'react/react-in-jsx-scope': 'off',
@@ -33,6 +35,20 @@ export default defineConfig([
3335
],
3436
},
3537
},
38+
{
39+
files: ['src/**/*.{ts,tsx}', 'example/src/**/*.{ts,tsx}'],
40+
plugins: { '@typescript-eslint': fixupPluginRules(tsPlugin) },
41+
languageOptions: {
42+
parser: tsParser,
43+
parserOptions: {
44+
projectService: true,
45+
tsconfigRootDir: __dirname,
46+
},
47+
},
48+
rules: {
49+
'@typescript-eslint/no-deprecated': 'error',
50+
},
51+
},
3652
{
3753
ignores: [
3854
'node_modules/',

example/src/demos/DataBindingArtboardsExample.tsx

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ import {
55
ActivityIndicator,
66
Pressable,
77
} from 'react-native';
8-
import { useState, useMemo, useEffect, useRef } from 'react';
8+
import { useState, useEffect, useRef } from 'react';
99
import {
1010
Fit,
1111
RiveView,
1212
useRiveFile,
13+
useViewModelInstance,
1314
type RiveFile,
1415
type BindableArtboard,
1516
} from '@rive-app/react-native';
@@ -77,16 +78,7 @@ function ArtboardSwapper({
7778
mainFile: RiveFile;
7879
assetsFile: RiveFile;
7980
}) {
80-
// Get the view model from the "Main" artboard and create an instance
81-
// IMPORTANT: Must memoize to prevent creating new instance on every render
82-
const viewModel = useMemo(
83-
() => mainFile.defaultArtboardViewModel(),
84-
[mainFile]
85-
);
86-
const instance = useMemo(
87-
() => viewModel?.createDefaultInstance(),
88-
[viewModel]
89-
);
81+
const instance = useViewModelInstance(mainFile);
9082
const [currentArtboard, setCurrentArtboard] = useState<string>('Dragon');
9183
const initializedRef = useRef(false);
9284

@@ -134,14 +126,10 @@ function ArtboardSwapper({
134126
}
135127
};
136128

137-
if (!instance || !viewModel) {
129+
if (!instance) {
138130
return (
139131
<View style={styles.container}>
140-
<Text style={styles.errorText}>
141-
{!viewModel
142-
? 'No view model found in main file'
143-
: 'Failed to create instance'}
144-
</Text>
132+
<ActivityIndicator size="large" color="#0000ff" />
145133
</View>
146134
);
147135
}

example/src/demos/QuickStart.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export default function QuickStart() {
2424
);
2525
const { riveViewRef, setHybridRef } = useRive();
2626
const viewModelInstance = useViewModelInstance(riveFile, {
27-
onInit: (vmi) => (vmi.numberProperty('health')!.value = 9),
27+
onInit: (vmi) => vmi.numberProperty('health')!.set(9),
2828
});
2929

3030
const { setValue: setHealth } = useRiveNumber('health', viewModelInstance);

example/src/exercisers/FontFallbackExample.tsx

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useMemo, useEffect } from 'react';
1+
import { useState, useEffect } from 'react';
22
import {
33
View,
44
Text,
@@ -15,6 +15,7 @@ import {
1515
useRive,
1616
useRiveFile,
1717
useRiveString,
18+
useViewModelInstance,
1819
type FontSource,
1920
type FallbackFont,
2021
} from '@rive-app/react-native';
@@ -257,14 +258,7 @@ function MountedView({ text }: { text: string }) {
257258
// https://rive.app/marketplace/26480-49641-simple-test-text-property/
258259
require('../../assets/rive/font_fallback.riv')
259260
);
260-
const viewModel = useMemo(
261-
() => riveFile?.defaultArtboardViewModel(),
262-
[riveFile]
263-
);
264-
const instance = useMemo(
265-
() => viewModel?.createDefaultInstance(),
266-
[viewModel]
267-
);
261+
const instance = useViewModelInstance(riveFile ?? null);
268262

269263
const { setValue: setRiveText, error: textError } = useRiveString(
270264
TEXT_PROPERTY,
@@ -280,20 +274,18 @@ function MountedView({ text }: { text: string }) {
280274
riveViewRef?.playIfNeeded();
281275
}, [text, setRiveText, riveViewRef]);
282276

283-
if (isLoading) {
277+
if (isLoading || !instance) {
284278
return (
285279
<View style={styles.riveContainer}>
286280
<ActivityIndicator size="large" color="#007AFF" />
287281
</View>
288282
);
289283
}
290284

291-
if (error || !riveFile || !instance) {
285+
if (error || !riveFile) {
292286
return (
293287
<View style={styles.riveContainer}>
294-
<Text style={styles.errorText}>
295-
{error || 'Failed to set up view model'}
296-
</Text>
288+
<Text style={styles.errorText}>{error || 'Failed to load file'}</Text>
297289
</View>
298290
);
299291
}

example/src/exercisers/ManyViewModels.tsx

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { StyleSheet, View, Text, TouchableOpacity, Button } from 'react-native';
2-
import { useState, useMemo, useRef, useEffect } from 'react';
2+
import { useState, useRef, useEffect } from 'react';
33
import type { Metadata } from '../shared/metadata';
44
import {
55
DataBindMode,
@@ -84,17 +84,28 @@ export default function ManyViewModels() {
8484
const riveViewRef = useRef<RiveViewRef>(undefined);
8585
const isListening = useRef(false);
8686

87-
// Create a ViewModelInstance for "green" to demonstrate instance binding
88-
const greenInstance = useMemo(() => {
89-
if (!riveFile) return undefined;
90-
try {
91-
const viewModel = riveFile.defaultArtboardViewModel();
92-
if (!viewModel) return undefined;
93-
return viewModel.createInstanceByName('green');
94-
} catch (e) {
95-
console.error('Failed to create green instance:', e);
96-
return undefined;
87+
const [greenInstance, setGreenInstance] = useState<
88+
ViewModelInstance | undefined
89+
>(undefined);
90+
91+
useEffect(() => {
92+
if (!riveFile) return;
93+
let cancelled = false;
94+
95+
async function setup() {
96+
const viewModel = await riveFile!.defaultArtboardViewModelAsync();
97+
if (cancelled || !viewModel) return;
98+
const instance = await viewModel.createInstanceByNameAsync('green');
99+
if (!cancelled) setGreenInstance(instance ?? undefined);
97100
}
101+
102+
setup().catch((e) => {
103+
if (!cancelled) console.error('Failed to create green instance:', e);
104+
});
105+
106+
return () => {
107+
cancelled = true;
108+
};
98109
}, [riveFile]);
99110

100111
const handleLoadImage = async () => {

example/src/exercisers/MenuListExample.tsx

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import {
77
TextInput,
88
ScrollView,
99
} from 'react-native';
10-
import { useRef, useMemo } from 'react';
10+
import { useRef, useState, useEffect } from 'react';
1111
import {
1212
Fit,
1313
RiveView,
14+
type ViewModel,
1415
type ViewModelInstance,
1516
type RiveFile,
1617
useRiveFile,
@@ -79,15 +80,27 @@ function MenuListContent({
7980
error,
8081
} = useRiveList('menu', instance);
8182

82-
const listItemViewModel = useMemo(
83-
() => file.viewModelByName('listItem'),
84-
[file]
85-
);
83+
const [listItemViewModel, setListItemViewModel] = useState<
84+
ViewModel | undefined
85+
>(undefined);
86+
87+
useEffect(() => {
88+
let cancelled = false;
89+
file
90+
.viewModelByNameAsync('listItem')
91+
.then((vm) => {
92+
if (!cancelled) setListItemViewModel(vm ?? undefined);
93+
})
94+
.catch((e) => console.error('Failed to load listItem view model:', e));
95+
return () => {
96+
cancelled = true;
97+
};
98+
}, [file]);
8699

87-
const addNewMenuItem = (label: string) => {
100+
const addNewMenuItem = async (label: string) => {
88101
if (!listItemViewModel) return;
89102

90-
const newMenuItemVmi = listItemViewModel.createInstance();
103+
const newMenuItemVmi = await listItemViewModel.createBlankInstanceAsync();
91104
if (!newMenuItemVmi) return;
92105

93106
const labelProperty = newMenuItemVmi.stringProperty('label');
@@ -96,9 +109,9 @@ function MenuListContent({
96109

97110
if (!labelProperty || !hoverColorProperty || !fontIconProperty) return;
98111

99-
labelProperty.value = label;
100-
hoverColorProperty.value = 0xff323232;
101-
fontIconProperty.value = '';
112+
labelProperty.set(label);
113+
hoverColorProperty.set(0xff323232);
114+
fontIconProperty.set('');
102115

103116
lastAdded.current = newMenuItemVmi;
104117
addInstance(newMenuItemVmi);
@@ -126,7 +139,7 @@ function MenuListContent({
126139
const menuItemLabel = menuItem.stringProperty('label');
127140
if (!menuItemLabel) return;
128141

129-
menuItemLabel.value = label;
142+
menuItemLabel.set(label);
130143
};
131144

132145
return (

example/src/exercisers/NestedViewModelExample.tsx

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ import {
66
Button,
77
TextInput,
88
} from 'react-native';
9-
import { useMemo, useRef, useState } from 'react';
9+
import { useRef, useState } from 'react';
1010
import {
1111
Fit,
1212
RiveView,
1313
useRiveFile,
1414
useRiveString,
15+
useViewModelInstance,
1516
type ViewModelInstance,
1617
type RiveFile,
1718
type RiveViewRef,
@@ -37,20 +38,10 @@ export default function NestedViewModelExample() {
3738
}
3839

3940
function WithViewModelSetup({ file }: { file: RiveFile }) {
40-
const viewModel = useMemo(() => file.defaultArtboardViewModel(), [file]);
41-
const instance = useMemo(
42-
() => viewModel?.createDefaultInstance(),
43-
[viewModel]
44-
);
41+
const instance = useViewModelInstance(file);
4542

46-
if (!instance || !viewModel) {
47-
return (
48-
<Text style={styles.errorText}>
49-
{!viewModel
50-
? 'No view model found'
51-
: 'Failed to create view model instance'}
52-
</Text>
53-
);
43+
if (!instance) {
44+
return <ActivityIndicator size="large" color="#0000ff" />;
5445
}
5546

5647
return <ReplaceViewModelTest instance={instance} file={file} />;
@@ -88,11 +79,10 @@ function ReplaceViewModelTest({
8879

8980
const addLog = (msg: string) => setLog((prev) => [...prev, msg]);
9081

91-
const handleReplace = () => {
92-
// Get vm2's instance
93-
const vm2Instance = instance.viewModel('vm2');
82+
const handleReplace = async () => {
83+
const vm2Instance = await instance.viewModelAsync('vm2');
9484
if (!vm2Instance) {
95-
addLog('❌ viewModel("vm2") returned undefined');
85+
addLog('❌ viewModelAsync("vm2") returned undefined');
9686
return;
9787
}
9888
addLog(`✅ Got vm2 instance: ${vm2Instance.instanceName}`);

example/src/exercisers/RiveDataBindingExample.tsx

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { View, Text, StyleSheet, ActivityIndicator } from 'react-native';
2-
import { useEffect, useMemo } from 'react';
2+
import { useEffect } from 'react';
33
import {
44
Fit,
55
RiveView,
66
useRiveNumber,
7+
useViewModelInstance,
78
type ViewModelInstance,
89
type RiveFile,
910
useRiveString,
@@ -34,20 +35,10 @@ export default function WithRiveFile() {
3435
}
3536

3637
function WithViewModelSetup({ file }: { file: RiveFile }) {
37-
const viewModel = useMemo(() => file.defaultArtboardViewModel(), [file]);
38-
const instance = useMemo(
39-
() => viewModel?.createDefaultInstance(),
40-
[viewModel]
41-
);
38+
const instance = useViewModelInstance(file);
4239

43-
if (!instance || !viewModel) {
44-
return (
45-
<Text style={styles.errorText}>
46-
{!viewModel
47-
? 'No view model found'
48-
: 'Failed to create view model instance'}
49-
</Text>
50-
);
40+
if (!instance) {
41+
return <ActivityIndicator size="large" color="#0000ff" />;
5142
}
5243

5344
return <DataBindingExample instance={instance} file={file} />;

example/src/exercisers/RiveEventsExample.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable @typescript-eslint/no-deprecated */
12
import { View, Text, StyleSheet, ActivityIndicator } from 'react-native';
23
import { useState, useEffect } from 'react';
34
import {

example/src/exercisers/RiveStateMachineInputsExample.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable @typescript-eslint/no-deprecated */
12
import { View, Text, StyleSheet, ActivityIndicator } from 'react-native';
23
import { useEffect } from 'react';
34
import { Fit, RiveView, useRive, useRiveFile } from '@rive-app/react-native';

0 commit comments

Comments
 (0)