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