diff --git a/example/package.json b/example/package.json
index 5139471f..862fc420 100644
--- a/example/package.json
+++ b/example/package.json
@@ -18,14 +18,15 @@
"@react-native-picker/picker": "^2.11.4",
"@react-navigation/native": "^7.1.9",
"@react-navigation/stack": "^7.3.2",
+ "@tanstack/react-query": "^5.100.10",
"react": "19.0.0",
"react-native": "0.79.2",
"react-native-gesture-handler": "2.29.1",
"react-native-nitro-modules": "0.35.0",
- "react-native-reanimated": "4.1.5",
+ "react-native-reanimated": "4.2.0",
"react-native-safe-area-context": "^5.4.0",
"react-native-screens": "~4.18.0",
- "react-native-worklets": "0.6.1"
+ "react-native-worklets": "0.7.4"
},
"devDependencies": {
"@babel/core": "^7.25.2",
diff --git a/example/src/App.tsx b/example/src/App.tsx
index 0d193a0a..91e0b9eb 100644
--- a/example/src/App.tsx
+++ b/example/src/App.tsx
@@ -11,6 +11,7 @@ import {
import { NavigationContainer, useNavigation } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import AsyncStorage from '@react-native-async-storage/async-storage';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { PagesList, type PageItem } from './PagesList';
import { HomeMenu } from './shared/HomeMenu';
@@ -95,38 +96,42 @@ function HomeScreen({ navigation }: { navigation: any }) {
);
}
+const queryClient = new QueryClient();
+
export default function App() {
return (
-
-
-
+
+
- {PagesList.map(({ id, component, name }) => (
+ >
- ))}
-
-
+ {PagesList.map(({ id, component, name }) => (
+
+ ))}
+
+
+
);
}
diff --git a/example/src/exercisers/RiveToReactNativeExample.tsx b/example/src/exercisers/RiveToReactNativeExample.tsx
index 6eec4f5a..fa45ed7b 100644
--- a/example/src/exercisers/RiveToReactNativeExample.tsx
+++ b/example/src/exercisers/RiveToReactNativeExample.tsx
@@ -7,13 +7,13 @@ import {
Switch,
} from 'react-native';
import { useEffect, useMemo, useState } from 'react';
+import { useQuery } from '@tanstack/react-query';
import Animated, {
- runOnUI,
useSharedValue,
useAnimatedStyle,
type SharedValue,
} from 'react-native-reanimated';
-import { NitroModules } from 'react-native-nitro-modules';
+import { scheduleOnUI } from 'react-native-worklets';
import {
Fit,
RiveView,
@@ -29,7 +29,7 @@ declare global {
var __callMicrotasks: () => void;
}
-installWorkletDispatcher(runOnUI);
+installWorkletDispatcher(scheduleOnUI);
function useRiveNumberListener(
property: ViewModelNumberProperty | undefined,
@@ -40,18 +40,16 @@ function useRiveNumberListener(
if (!property) return;
if (useUIThread) {
- const boxedProperty = NitroModules.box(property);
const sv = sharedValue;
- runOnUI(() => {
+ scheduleOnUI(() => {
'worklet';
- const prop = boxedProperty.unbox();
- prop.addListener((value: number) => {
+ property.addListener((value: number) => {
'worklet';
sv.value = value;
global.__callMicrotasks();
});
- })();
+ });
return () => {
property.removeListeners();
@@ -78,32 +76,40 @@ export default function RiveToReactNativeExample() {
) : riveFile ? (
) : (
- {error?.message || 'Unexpected error'}
+
+ {error?.message || 'Unexpected error'}
+
)}
);
}
function WithViewModelSetup({ file }: { file: RiveFile }) {
- const viewModel = useMemo(() => file.defaultArtboardViewModel(), [file]);
- const instance = useMemo(
- () => viewModel?.createDefaultInstance(),
- [viewModel]
- );
const [useUIThread, setUseUIThread] = useState(true);
- if (!instance || !viewModel) {
+ const { data: instance, error } = useQuery({
+ queryKey: ['bouncing-ball-instance', file],
+ queryFn: async () => {
+ const vm = await file.defaultArtboardViewModelAsync();
+ if (!vm) throw new Error('No view model found.');
+ const inst = await vm.createDefaultInstanceAsync();
+ if (!inst) throw new Error('Failed to create view model instance');
+ return inst;
+ },
+ });
+
+ if (error) {
return (
-
- {!viewModel
- ? 'No view model found.'
- : 'Failed to create view model instance'}
-
+ {error.message}
);
}
+ if (!instance) {
+ return ;
+ }
+
return (
= 0.7.1 for automatic HybridObject serialization.
+ *
+ * @param scheduleOnUI - The scheduleOnUI function from react-native-worklets
*
* @example
* ```tsx
* import { installWorkletDispatcher } from '@rive-app/react-native';
- * import { runOnUI } from 'react-native-reanimated';
+ * import { scheduleOnUI } from 'react-native-worklets';
*
* // Call once at app startup
- * installWorkletDispatcher(runOnUI);
+ * installWorkletDispatcher(scheduleOnUI);
* ```
*/
export function installWorkletDispatcher(
- runOnUI: (
- worklet: (...args: Args) => ReturnValue
- ) => (...args: Args) => void
+ scheduleOnUI: (
+ worklet: (...args: Args) => ReturnValue,
+ ...args: Args
+ ) => void
): void {
if (isInstalled) {
return;
@@ -34,11 +37,8 @@ export function installWorkletDispatcher(
const bridge =
NitroModules.createHybridObject('RiveWorkletBridge');
- const boxedBridge = NitroModules.box(bridge);
-
- runOnUI(() => {
+ scheduleOnUI(() => {
'worklet';
- const b = boxedBridge.unbox();
- b.install();
- })();
+ bridge.install();
+ });
}
diff --git a/yarn.lock b/yarn.lock
index 6d507750..7cba3eff 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5563,6 +5563,24 @@ __metadata:
languageName: node
linkType: hard
+"@tanstack/query-core@npm:5.100.10":
+ version: 5.100.10
+ resolution: "@tanstack/query-core@npm:5.100.10"
+ checksum: d19351b858bd758ad8a860e9333c9b78c1ec4a564b867f592046a60cf8421580d8aefcb3dfc3c67d59c5f8cf3dee9a3074ac07204e37ec54012fd912246d0dbd
+ languageName: node
+ linkType: hard
+
+"@tanstack/react-query@npm:^5.100.10":
+ version: 5.100.10
+ resolution: "@tanstack/react-query@npm:5.100.10"
+ dependencies:
+ "@tanstack/query-core": 5.100.10
+ peerDependencies:
+ react: ^18 || ^19
+ checksum: 90ceb6cafa573a081bcaf03e727ab99e68c71464dc65cede626c3c10c848d136f33ffb6523a04c477448d46d320b202b5d93fbb6f1cb4bef27fd3af3ff43cbee
+ languageName: node
+ linkType: hard
+
"@testing-library/react-hooks@npm:^8.0.1":
version: 8.0.1
resolution: "@testing-library/react-hooks@npm:8.0.1"
@@ -16321,6 +16339,20 @@ __metadata:
languageName: node
linkType: hard
+"react-native-reanimated@npm:4.2.0":
+ version: 4.2.0
+ resolution: "react-native-reanimated@npm:4.2.0"
+ dependencies:
+ react-native-is-edge-to-edge: 1.2.1
+ semver: 7.7.3
+ peerDependencies:
+ react: "*"
+ react-native: "*"
+ react-native-worklets: ">=0.7.0"
+ checksum: a8a4c321513cdca93a66b90c284c86eb56b05ac7d8989cd36b3e090055b59b9613870891868432835a13d1ddcaad3b1763bb9c7ed267616d90e138625d15ef23
+ languageName: node
+ linkType: hard
+
"react-native-reanimated@npm:4.2.1":
version: 4.2.1
resolution: "react-native-reanimated@npm:4.2.1"
@@ -16354,6 +16386,7 @@ __metadata:
"@react-native/typescript-config": 0.79.2
"@react-navigation/native": ^7.1.9
"@react-navigation/stack": ^7.3.2
+ "@tanstack/react-query": ^5.100.10
"@types/deep-equal": ^1.0.4
"@types/react": ^19.0.0
babel-plugin-react-compiler: ^1.0.0
@@ -16364,10 +16397,10 @@ __metadata:
react-native-gesture-handler: 2.29.1
react-native-harness: 1.0.0
react-native-nitro-modules: 0.35.0
- react-native-reanimated: 4.1.5
+ react-native-reanimated: 4.2.0
react-native-safe-area-context: ^5.4.0
react-native-screens: ~4.18.0
- react-native-worklets: 0.6.1
+ react-native-worklets: 0.7.4
languageName: unknown
linkType: soft