Skip to content

Commit 4c610e8

Browse files
committed
refactor: streamline RiveDataBindingExample with 3-component pattern
This refactor simplifies the data binding example by: - Splitting logic into 3 focused components: 1. WithRiveFile: Handles file loading 2. WithViewModelSetup: Creates view model and instance using useMemo 3. DataBindingExample: Manages data bindings and rendering - Reducing useEffect usage from 7 to 2: - One for binding the instance to RiveView - One for setting initial property values - Replacing useState + useEffect pattern with useMemo for: - View model creation - View model instance creation - Removing debug logging useEffects that tracked value changes - Simplifying error handling by using conditional rendering This pattern is cleaner, more maintainable, and easier to understand while demonstrating the same data binding capabilities.
1 parent 99b12c1 commit 4c610e8

1 file changed

Lines changed: 73 additions & 117 deletions

File tree

example/src/pages/RiveDataBindingExample.tsx

Lines changed: 73 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,159 +1,115 @@
11
import { View, Text, StyleSheet, ActivityIndicator } from 'react-native';
2-
import { useState, useEffect } from 'react';
2+
import { useEffect, useMemo } from 'react';
33
import {
44
Fit,
55
RiveView,
66
useRive,
77
useRiveNumber,
88
type ViewModelInstance,
9+
type RiveFile,
910
useRiveString,
1011
useRiveColor,
1112
useRiveTrigger,
1213
useRiveFile,
1314
} from 'react-native-rive';
1415

15-
export default function DataBindingExample() {
16-
const { riveViewRef, setHybridRef } = useRive();
17-
const [viewModelInstance, setViewModelInstance] =
18-
useState<ViewModelInstance | null>(null);
19-
const [viewModelError, setViewModelError] = useState<string | null>(null);
20-
16+
export default function WithRiveFile() {
2117
const { riveFile, isLoading, error } = useRiveFile(
2218
require('../../assets/rive/rewards_source.riv')
2319
);
2420

25-
// Create view model instance when Rive file is loaded
26-
useEffect(() => {
27-
if (riveFile) {
28-
try {
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);
39-
} catch (err) {
40-
setViewModelError(
41-
err instanceof Error
42-
? err.message
43-
: 'Failed to create view model instance'
44-
);
45-
setViewModelInstance(null);
46-
}
47-
}
48-
}, [riveFile]);
21+
return (
22+
<View style={styles.container}>
23+
<View style={styles.riveContainer}>
24+
{isLoading ? (
25+
<ActivityIndicator size="large" color="#0000ff" />
26+
) : riveFile ? (
27+
<WithViewModelSetup file={riveFile} />
28+
) : (
29+
<Text style={styles.errorText}>{error || 'Unexpected error'}</Text>
30+
)}
31+
</View>
32+
</View>
33+
);
34+
}
4935

50-
// Cleanup view model instance when component unmounts or the view model instance changes
51-
useEffect(() => {
52-
return () => {
53-
viewModelInstance?.dispose();
54-
};
55-
}, [viewModelInstance]);
36+
function WithViewModelSetup({ file }: { file: RiveFile }) {
37+
const viewModel = useMemo(() => file.defaultArtboardViewModel(), [file]);
38+
const instance = useMemo(
39+
() => viewModel?.createDefaultInstance(),
40+
[viewModel]
41+
);
5642

57-
// Bind the view model instance to the RiveView
58-
useEffect(() => {
59-
if (viewModelInstance && riveViewRef) {
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-
}
66-
}
67-
}, [viewModelInstance, riveViewRef]);
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+
);
51+
}
52+
53+
return <DataBindingExample instance={instance} file={file} />;
54+
}
55+
56+
function DataBindingExample({
57+
instance,
58+
file,
59+
}: {
60+
instance: ViewModelInstance;
61+
file: RiveFile;
62+
}) {
63+
const { riveViewRef, setHybridRef } = useRive();
6864

69-
// Databinding: Number
70-
const { value: coinValue, error: coinValueError } = useRiveNumber(
71-
'Coin/Item_Value',
72-
viewModelInstance
73-
);
7465
useEffect(() => {
75-
if (coinValue !== undefined) {
76-
console.log('coinValue', coinValue);
66+
if (riveViewRef) {
67+
riveViewRef.bindViewModelInstance(instance);
7768
}
78-
}, [coinValue]);
69+
}, [riveViewRef, instance]);
70+
71+
const { error: coinValueError } = useRiveNumber('Coin/Item_Value', instance);
7972

8073
if (coinValueError) {
8174
console.error('coinValueError', coinValueError);
8275
}
8376

84-
// Databinding: String
85-
const { value: buttonText, setValue: setButtonText } = useRiveString(
86-
'Button/State_1',
87-
viewModelInstance
77+
const { setValue: setButtonText } = useRiveString('Button/State_1', instance);
78+
79+
const { setValue: setBarColor, error: barColorError } = useRiveColor(
80+
'Energy_Bar/Bar_Color',
81+
instance
8882
);
89-
useEffect(() => {
90-
if (buttonText) {
91-
console.log('buttonText', buttonText);
92-
}
93-
}, [buttonText]);
94-
95-
// Databinding: Color
96-
const {
97-
value: barColor,
98-
setValue: setBarColor,
99-
error: barColorError,
100-
} = useRiveColor('Energy_Bar/Bar_Color', viewModelInstance);
83+
10184
if (barColorError) {
10285
console.error('barColorError', barColorError);
10386
}
10487

105-
useEffect(() => {
106-
if (barColor) {
107-
console.log('barColor', barColor);
108-
}
109-
}, [barColor]);
110-
111-
// Databinding: Trigger
112-
const { error: triggerError } = useRiveTrigger(
113-
'Button/Pressed',
114-
viewModelInstance,
115-
{
116-
onTrigger: () => {
117-
console.log('Button pressed');
118-
},
119-
}
120-
);
88+
const { error: triggerError } = useRiveTrigger('Button/Pressed', instance, {
89+
onTrigger: () => {
90+
console.log('Button pressed');
91+
},
92+
});
93+
12194
if (triggerError) {
12295
console.error('triggerError', triggerError);
12396
}
12497

125-
// Set the initial values of the properties
12698
useEffect(() => {
127-
if (viewModelInstance) {
128-
try {
129-
setButtonText("Let's go!");
130-
setBarColor('#0000FF');
131-
} catch (err) {
132-
console.error('Failed to set initial values:', err);
133-
}
134-
}
135-
}, [setBarColor, setButtonText, viewModelInstance]);
99+
setButtonText("Let's go!");
100+
setBarColor('#0000FF');
101+
}, [setBarColor, setButtonText]);
136102

137103
return (
138-
<View style={styles.container}>
139-
<View style={styles.riveContainer}>
140-
{error || viewModelError ? (
141-
<Text style={styles.errorText}>{error || viewModelError}</Text>
142-
) : isLoading ? (
143-
<ActivityIndicator size="large" color="#0000ff" />
144-
) : (
145-
<RiveView
146-
style={styles.rive}
147-
autoBind={false}
148-
autoPlay={true}
149-
fit={Fit.Layout}
150-
layoutScaleFactor={1}
151-
file={riveFile!}
152-
hybridRef={setHybridRef}
153-
/>
154-
)}
155-
</View>
156-
</View>
104+
<RiveView
105+
style={styles.rive}
106+
autoBind={false}
107+
autoPlay={true}
108+
fit={Fit.Layout}
109+
layoutScaleFactor={1}
110+
file={file}
111+
hybridRef={setHybridRef}
112+
/>
157113
);
158114
}
159115

0 commit comments

Comments
 (0)