diff --git a/README.md b/README.md index 1262106..3e5a606 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,18 @@ Low overhead monitoring of important performance metrics for React Native apps l bun add react-native-performance-toolkit react-native-nitro-modules ``` +### Optional: Reanimated support + +If you want to use UI thread components and hooks (recommended for accurate FPS display), install the optional dependencies: + +```bash +bun add react-native-reanimated react-native-worklets react-native-gesture-handler +``` + ## Requirements - React Native v0.76.0 or higher -- Reanimated v4 or higher +- (Optional) Reanimated v4 or higher - for UI thread components and hooks ## Usage @@ -67,11 +75,13 @@ const SomeComponent = () => { ### Reanimated Hooks - UI Thread +> **Note:** These features require `react-native-reanimated` and `react-native-worklets` to be installed. Import from `react-native-performance-toolkit/reanimated`. + To avoid the issue with not showing 0 FPS, it's recommended to use Reanimated based hooks or pre-made components. This will ensure the value is updated even if the JS thread is blocked. ```tsx import { TextInput } from 'react-native' -import { useFpsJsSharedValue } from 'react-native-performance-toolkit' +import { useFpsJsSharedValue } from 'react-native-performance-toolkit/reanimated' import Animated, { useAnimatedReaction, useAnimatedRef, @@ -97,6 +107,8 @@ const SomeComponent = () => { ### Pre-made Reanimated components - UI Thread +> **Note:** These features require `react-native-reanimated`, `react-native-worklets`, and `react-native-gesture-handler` to be installed. Import from `react-native-performance-toolkit/reanimated`. + For better DX, the library provides pre-made Reanimated components that run solely on the UI thread. You can use either the convenience wrappers or the flexible base component: ```tsx @@ -106,7 +118,7 @@ import { CpuUsageCounter, MemoryUsageCounter, UIThreadReanimatedCounter, -} from 'react-native-performance-toolkit' +} from 'react-native-performance-toolkit/reanimated' const SomeComponent = () => { return ( @@ -157,6 +169,8 @@ console.log('Memory Usage:', getValueFromBuffer(memoryUsageBuffer)) ### Access from worklets (advanced usage) +> **Note:** This requires `react-native-reanimated` and `react-native-worklets` to be installed. + You can also access the value from any worklet thread, but to do that you need to use [Nitro Modules unboxing function](https://nitro.margelo.com/docs/worklets). For more detailed implementation look for [source code of UI Reanimated hooks like `useFpsJsSharedValue`](https://github.com/Nodonisko/react-native-performance-toolkit/blob/main/src/hooks/uiThreadHooks.ts). ```tsx @@ -187,6 +201,8 @@ const updateFps = useCallback(() => { ## API Reference +### Core API (no additional dependencies) + - **Simple getters** - `getJsFps(): number` - Returns current JS FPS (0-60) - `getUiFps(): number` - Returns current UI FPS (0-30/60/90/120/...) @@ -207,12 +223,6 @@ const updateFps = useCallback(() => { - `useCpuUsage(): number` - Hook that returns current CPU usage - `useMemoryUsage(): number` - Hook that returns current memory usage -- **React Components (runs on UI Thread)** - - `` - Pre-made component displaying JS FPS - - `` - Pre-made component displaying UI FPS - - `` - Pre-made component displaying CPU usage - - `` - Pre-made component displaying memory usage - - **Buffer-based API** - `getJsFpsBuffer(): ArrayBuffer` - Returns ArrayBuffer with JS FPS data - `getUiFpsBuffer(): ArrayBuffer` - Returns ArrayBuffer with UI FPS data @@ -229,6 +239,25 @@ const updateFps = useCallback(() => { - `getDeviceMaxRefreshRate(): number` - `getDeviceCurrentRefreshRate(): number` +### Reanimated API (requires optional dependencies) + +Import from `react-native-performance-toolkit/reanimated`: + +- **React Components (runs on UI Thread)** + - `` - Pre-made component displaying JS FPS + - `` - Pre-made component displaying UI FPS + - `` - Pre-made component displaying CPU usage + - `` - Pre-made component displaying memory usage + - `` - Flexible base component + - `` - Draggable wrapper component + +- **Reanimated Hooks (UI Thread)** + - `useFpsJsSharedValue()` - Returns SharedValue with JS FPS + - `useFpsUiSharedValue()` - Returns SharedValue with UI FPS + - `useFpsCpuSharedValue()` - Returns SharedValue with CPU usage + - `useFpsMemorySharedValue()` - Returns SharedValue with memory usage + - `useCounterSharedValue(type)` - Generic hook for any counter type + ## Architecture ### Low overhead tracking diff --git a/bun.lock b/bun.lock index 8f8bcef..83ac989 100644 --- a/bun.lock +++ b/bun.lock @@ -21,8 +21,16 @@ "peerDependencies": { "react": "*", "react-native": "*", + "react-native-gesture-handler": "*", "react-native-nitro-modules": "*", + "react-native-reanimated": "*", + "react-native-worklets": "*", }, + "optionalPeers": [ + "react-native-gesture-handler", + "react-native-reanimated", + "react-native-worklets", + ], }, "example": { "name": "react-native-performance-toolkit-example", diff --git a/example/App.tsx b/example/App.tsx index a47c6d0..e5fe4d7 100644 --- a/example/App.tsx +++ b/example/App.tsx @@ -14,7 +14,7 @@ import { UIFpsCounter, CpuUsageCounter, MemoryUsageCounter, -} from 'react-native-performance-toolkit'; +} from 'react-native-performance-toolkit/reanimated'; function formatValue(value: number): string { return Number.isFinite(value) ? value.toFixed(0) : '0'; diff --git a/example/babel.config.js b/example/babel.config.js index 5ed92ed..7bca873 100644 --- a/example/babel.config.js +++ b/example/babel.config.js @@ -1,21 +1,7 @@ -const path = require('path'); -const pak = require('../package.json'); - module.exports = api => { api.cache(true); return { presets: ['module:@react-native/babel-preset'], - plugins: [ - [ - 'module-resolver', - { - extensions: ['.js', '.ts', '.json', '.jsx', '.tsx'], - alias: { - [pak.name]: path.join(__dirname, '../', pak.source), - }, - }, - ], - 'react-native-worklets/plugin', - ], + plugins: ['react-native-worklets/plugin'], }; }; diff --git a/example/metro.config.js b/example/metro.config.js index d268384..aeb22f7 100644 --- a/example/metro.config.js +++ b/example/metro.config.js @@ -1,5 +1,6 @@ const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config'); const path = require('path'); +const pak = require('../package.json'); const root = path.resolve(__dirname, '..'); /** @@ -10,6 +11,25 @@ const root = path.resolve(__dirname, '..'); */ const config = { watchFolders: [root], + resolver: { + resolveRequest: (context, moduleName, platform) => { + // Handle subpath import + if (moduleName === `${pak.name}/reanimated`) { + return { + filePath: path.resolve(__dirname, '../src/reanimated.tsx'), + type: 'sourceFile', + }; + } + // Handle main package import + if (moduleName === pak.name) { + return { + filePath: path.resolve(__dirname, '../src/index.tsx'), + type: 'sourceFile', + }; + } + return context.resolveRequest(context, moduleName, platform); + }, + }, }; module.exports = mergeConfig(getDefaultConfig(__dirname), config); \ No newline at end of file diff --git a/example/tsconfig.json b/example/tsconfig.json index f0eea67..afd76ab 100644 --- a/example/tsconfig.json +++ b/example/tsconfig.json @@ -6,7 +6,8 @@ "strict": true, "baseUrl": ".", "paths": { - "react-native-performance-toolkit": ["../src"] + "react-native-performance-toolkit": ["../src"], + "react-native-performance-toolkit/reanimated": ["../src/reanimated"] } } } \ No newline at end of file diff --git a/package.json b/package.json index e68b696..e51b045 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,8 @@ "ios/**/*.swift", "app.plugin.js", "*.podspec", + "reanimated.js", + "reanimated.d.ts", "README.md", "!example", "!media" @@ -79,6 +81,17 @@ "react-native-worklets": "*", "react-native-gesture-handler": "*" }, + "peerDependenciesMeta": { + "react-native-reanimated": { + "optional": true + }, + "react-native-worklets": { + "optional": true + }, + "react-native-gesture-handler": { + "optional": true + } + }, "eslintConfig": { "root": true, "extends": [ diff --git a/reanimated.d.ts b/reanimated.d.ts new file mode 100644 index 0000000..ca8805d --- /dev/null +++ b/reanimated.d.ts @@ -0,0 +1 @@ +export * from './lib/typescript/src/reanimated' diff --git a/reanimated.js b/reanimated.js new file mode 100644 index 0000000..fd74978 --- /dev/null +++ b/reanimated.js @@ -0,0 +1 @@ +module.exports = require('./lib/commonjs/reanimated.js') diff --git a/src/index.tsx b/src/index.tsx index 67760fd..f3f9367 100755 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,7 +1,5 @@ import './specs/TurboPerformanceToolkit' -import React from 'react' -import { UIThreadReanimatedCounter } from './components/UIThreadReanimatedCounter' import { PerformanceToolkit } from './hybrids' export { @@ -11,21 +9,6 @@ export { PerformanceToolkit, } from './hybrids' -export const JSFpsCounter = () => { - return -} - -export const UIFpsCounter = () => { - return -} - -export const CpuUsageCounter = () => { - return -} -export const MemoryUsageCounter = () => { - return -} - export const getDeviceMaxRefreshRate = () => PerformanceToolkit.getDeviceMaxRefreshRate() diff --git a/src/reanimated.tsx b/src/reanimated.tsx new file mode 100644 index 0000000..ddf9073 --- /dev/null +++ b/src/reanimated.tsx @@ -0,0 +1,22 @@ +import React from 'react' +import { UIThreadReanimatedCounter } from './components/UIThreadReanimatedCounter' + +// Reanimated-dependent components +export const JSFpsCounter = () => { + return +} + +export const UIFpsCounter = () => { + return +} + +export const CpuUsageCounter = () => { + return +} + +export const MemoryUsageCounter = () => { + return +} + +export { DraggableView } from './components/DraggableView' +export * from './hooks/uiThreadHooks'