diff --git a/docs/docs/guides/EXPOSURE.mdx b/docs/docs/guides/EXPOSURE.mdx index 563ea4a402..5ed2c5f4cb 100644 --- a/docs/docs/guides/EXPOSURE.mdx +++ b/docs/docs/guides/EXPOSURE.mdx @@ -49,12 +49,12 @@ Just like [`zoom`](zooming), this property can be animated using Reanimated. return interpolate(exposureSlider.value, [-1, 0, 1], [device.minExposure, 0, device.maxExposure]) - }, [exposureSlider, device]) + }) // 3. pass it as an animated prop const animatedProps = useAnimatedProps(() => ({ exposure: exposureValue.value - }), [exposureValue]) + })) // 4. render Camera return ( diff --git a/docs/docs/guides/FRAME_PROCESSORS_CREATE_OVERVIEW.mdx b/docs/docs/guides/FRAME_PROCESSORS_CREATE_OVERVIEW.mdx index ef8e77964a..a53781fcd2 100644 --- a/docs/docs/guides/FRAME_PROCESSORS_CREATE_OVERVIEW.mdx +++ b/docs/docs/guides/FRAME_PROCESSORS_CREATE_OVERVIEW.mdx @@ -21,7 +21,7 @@ function App() { // highlight-next-line const faces = detectFaces(frame) console.log(`Faces in Frame: ${faces}`) - }, []) + }) return ( @@ -91,7 +91,7 @@ const frameProcessor = useFrameProcessor((frame) => { // by downscaling the frame, the `detectObjects` function runs faster. const objects = detectObjects(resizedFrame) console.log(objects) -}, []) +}) ``` ### Parameters @@ -102,7 +102,7 @@ Frame Processors can also accept parameters, following the same type convention const frameProcessor = useFrameProcessor((frame) => { 'worklet' const faces = scanFaces(frame, { accuracy: 'fast' }) -}, []) +}) ``` ### Exceptions @@ -131,7 +131,7 @@ const frameProcessor = useFrameProcessor((frame) => { } catch (e) { console.log(`Error: ${e.message}`) } -}, []) +}) ``` ## What's possible? @@ -177,7 +177,7 @@ function App() { const frameProcessor = useFrameProcessor((frame) => { 'worklet' SomeAI.process(frame) // does not block frame processor, runs async - }, []) + }) useEffect(() => { SomeAI.addListener((results) => { diff --git a/docs/docs/guides/FRAME_PROCESSORS_CREATE_PLUGIN_CPP.mdx b/docs/docs/guides/FRAME_PROCESSORS_CREATE_PLUGIN_CPP.mdx index ff561d2e8c..dbe5e293dd 100644 --- a/docs/docs/guides/FRAME_PROCESSORS_CREATE_PLUGIN_CPP.mdx +++ b/docs/docs/guides/FRAME_PROCESSORS_CREATE_PLUGIN_CPP.mdx @@ -49,7 +49,7 @@ const frameProcessor = useFrameProcessor((frame) => { 'worklet' const result = global.myCppPlugin(frame) console.log(`C++ result: ${result}`) // <-- 42 -}, []) +}) ``` To include VisionCamera's C++ library in your plugin's C++ code, you need to link it and include the headers. diff --git a/docs/docs/guides/FRAME_PROCESSORS_INTERACTING.mdx b/docs/docs/guides/FRAME_PROCESSORS_INTERACTING.mdx index 72e4c42aea..b72f13260c 100644 --- a/docs/docs/guides/FRAME_PROCESSORS_INTERACTING.mdx +++ b/docs/docs/guides/FRAME_PROCESSORS_INTERACTING.mdx @@ -30,7 +30,7 @@ const frameProcessor = useFrameProcessor((frame) => { const objects = detectObjects(frame) const bananas = objects.filter((o) => o.type === targetObject) console.log(`Detected ${bananas} bananas!`) -}, [targetObject]) +}) ``` ### Shared Values @@ -45,7 +45,7 @@ const frameProcessor = useFrameProcessor((frame) => { 'worklet' const objects = detectObjects(frame) bananas.value = objects.filter((o) => o.type === 'banana') -}, [bananas]) +}) // Draw bananas in a Skia Canvas const onDraw = useDrawCallback((canvas) => { @@ -76,7 +76,7 @@ const frameProcessor = useFrameProcessor((frame) => { if (faces.length > 0) { onFaceDetected(faces[0]) } -}, [onFaceDetected]) +}) ``` ## Threading @@ -101,7 +101,7 @@ const frameProcessor = useFrameProcessor((frame) => { console.log("I'm running asynchronously, possibly at a lower FPS rate!") const faces = detectFaces(frame) }) -}, []) +}) ``` ### Running at a throttled FPS rate @@ -118,7 +118,7 @@ const frameProcessor = useFrameProcessor((frame) => { console.log("I'm running synchronously at 2 FPS!") const brightness = detectBrightness(frame) }) -}, []) +}) ``` #### 🚀 Next section: [Zooming](/docs/guides/zooming) (or [creating a Frame Processor Plugin](/docs/guides/frame-processors-plugins-overview)) diff --git a/docs/docs/guides/FRAME_PROCESSORS_TIPS.mdx b/docs/docs/guides/FRAME_PROCESSORS_TIPS.mdx index 4776272c32..b2fda6fda0 100644 --- a/docs/docs/guides/FRAME_PROCESSORS_TIPS.mdx +++ b/docs/docs/guides/FRAME_PROCESSORS_TIPS.mdx @@ -38,10 +38,6 @@ If you use native Frame Processor Plugins, make sure they are optimized for real - Prefer plugins that support **GPU acceleration**. For Tensorflow, this might be the CoreML or Metal GPU delegates - For operations such as resizing, **prefer GPU or CPU vector acceleration** (e.g. Accelerate/vImage) instead of just array loops -## ESLint react-hooks plugin - -If you are using the [react-hooks ESLint plugin](https://www.npmjs.com/package/eslint-plugin-react-hooks), make sure to add `useFrameProcessor` and `useSkiaFrameProcessor` to `additionalHooks` inside your ESLint config so dependencies are detected properly. (See ["advanced configuration"](https://www.npmjs.com/package/eslint-plugin-react-hooks#advanced-configuration)) - ## Technical ### Frame Processors diff --git a/docs/docs/guides/FRAME_PROCESSOR_CREATE_FINAL.mdx b/docs/docs/guides/FRAME_PROCESSOR_CREATE_FINAL.mdx index b60a235e5a..53fc83ee2f 100644 --- a/docs/docs/guides/FRAME_PROCESSOR_CREATE_FINAL.mdx +++ b/docs/docs/guides/FRAME_PROCESSOR_CREATE_FINAL.mdx @@ -34,7 +34,7 @@ function App() { // highlight-next-line const faces = scanFaces(frame) console.log(`Faces in Frame: ${faces}`) - }, []) + }) return ( diff --git a/docs/docs/guides/PIXEL_FORMATS.mdx b/docs/docs/guides/PIXEL_FORMATS.mdx index 431cecefb3..c5a37283d2 100644 --- a/docs/docs/guides/PIXEL_FORMATS.mdx +++ b/docs/docs/guides/PIXEL_FORMATS.mdx @@ -38,7 +38,7 @@ function App() { const frameProcessor = useFrameProcessor((frame) => { 'worklet' console.log(frame.pixelFormat) // <-- "rgb" - }, []) + }) return ( ( - () => ({ zoom: zoom.value }), - [zoom] - ) + const animatedProps = useAnimatedProps(() => ({ zoom: zoom.value })) // highlight-end if (device == null) return diff --git a/package/.eslintrc.js b/package/.eslintrc.js index 56b2157d6d..462ad2f7a4 100644 --- a/package/.eslintrc.js +++ b/package/.eslintrc.js @@ -74,14 +74,6 @@ module.exports = { '@typescript-eslint/no-non-null-assertion': 'error', '@typescript-eslint/no-unnecessary-condition': 'error', '@typescript-eslint/consistent-type-imports': 'warn', - - // react hooks - 'react-hooks/exhaustive-deps': [ - 'error', - { - additionalHooks: '(useDerivedValue|useAnimatedStyle|useAnimatedProps|useWorkletCallback|useFrameProcessor|useSkiaFrameProcessor)', - }, - ], }, env: { node: true, diff --git a/package/example/ios/Podfile.lock b/package/example/ios/Podfile.lock index da2ed6afb2..690530576f 100644 --- a/package/example/ios/Podfile.lock +++ b/package/example/ios/Podfile.lock @@ -337,7 +337,7 @@ PODS: - react-native-video/Video (= 5.2.1) - react-native-video/Video (5.2.1): - React-Core - - react-native-worklets-core (1.2.0): + - react-native-worklets-core (1.3.2): - React - React-callinvoker - React-Core @@ -684,7 +684,7 @@ SPEC CHECKSUMS: react-native-safe-area-context: 0ee144a6170530ccc37a0fd9388e28d06f516a89 react-native-skia: d368ae81d61c0253df36106ea76324b5a5519ddc react-native-video: c26780b224543c62d5e1b2a7244a5cd1b50e8253 - react-native-worklets-core: a316975bba20b73d47aa4d41bffb0ca984ba592e + react-native-worklets-core: ff4a4076ad86469e61ec8e39da2cb13c55da2385 React-NativeModulesApple: b6868ee904013a7923128892ee4a032498a1024a React-perflogger: 31ea61077185eb1428baf60c0db6e2886f141a5a React-RCTActionSheet: 392090a3abc8992eb269ef0eaa561750588fc39d diff --git a/package/example/package.json b/package/example/package.json index 9b8df2180d..2bf0e499ae 100644 --- a/package/example/package.json +++ b/package/example/package.json @@ -29,7 +29,7 @@ "react-native-static-safe-area-insets": "^2.2.0", "react-native-vector-icons": "^10.0.0", "react-native-video": "^5.2.1", - "react-native-worklets-core": "^1.2.0" + "react-native-worklets-core": "^1.3.2" }, "devDependencies": { "@babel/core": "^7.22.10", diff --git a/package/example/src/CameraPage.tsx b/package/example/src/CameraPage.tsx index 28ae31146c..c9ede28f9d 100644 --- a/package/example/src/CameraPage.tsx +++ b/package/example/src/CameraPage.tsx @@ -92,7 +92,7 @@ export function CameraPage({ navigation }: Props): React.ReactElement { return { zoom: z, } - }, [maxZoom, minZoom, zoom]) + }) //#endregion //#region Callbacks @@ -187,7 +187,7 @@ export function CameraPage({ navigation }: Props): React.ReactElement { examplePlugin(frame) exampleKotlinSwiftPlugin(frame) }) - }, []) + }) const videoHdr = format?.supportsVideoHdr && enableHdr const photoHdr = format?.supportsPhotoHdr && enableHdr && !videoHdr diff --git a/package/example/src/views/CaptureButton.tsx b/package/example/src/views/CaptureButton.tsx index ecd041da45..cf4b952288 100644 --- a/package/example/src/views/CaptureButton.tsx +++ b/package/example/src/views/CaptureButton.tsx @@ -196,20 +196,17 @@ const _CaptureButton: React.FC = ({ }) //#endregion - const shadowStyle = useAnimatedStyle( - () => ({ - transform: [ - { - scale: withSpring(isPressingButton.value ? 1 : 0, { - mass: 1, - damping: 35, - stiffness: 300, - }), - }, - ], - }), - [isPressingButton], - ) + const shadowStyle = useAnimatedStyle(() => ({ + transform: [ + { + scale: withSpring(isPressingButton.value ? 1 : 0, { + mass: 1, + damping: 35, + stiffness: 300, + }), + }, + ], + })) const buttonStyle = useAnimatedStyle(() => { let scale: number if (enabled) { @@ -246,7 +243,7 @@ const _CaptureButton: React.FC = ({ }, ], } - }, [enabled, isPressingButton]) + }) return ( =1.2.3", "react": "*", "react-native": "*", "react-native-reanimated": "*", - "react-native-worklets-core": "*" + "react-native-worklets-core": ">=1.3.2" }, "peerDependenciesMeta": { "react-native-worklets-core": { diff --git a/package/src/hooks/useFrameProcessor.ts b/package/src/hooks/useFrameProcessor.ts index a7348927be..f533e83d0b 100644 --- a/package/src/hooks/useFrameProcessor.ts +++ b/package/src/hooks/useFrameProcessor.ts @@ -1,8 +1,8 @@ -import type { DependencyList } from 'react' import { useMemo } from 'react' import { withFrameRefCounting } from '../frame-processors/withFrameRefCounting' import type { ReadonlyFrameProcessor } from '../types/CameraProps' import type { Frame } from '../types/Frame' +import { WorkletsProxy } from '../dependencies/WorkletsProxy' /** * Create a new Frame Processor function which you can pass to the ``. @@ -28,7 +28,6 @@ export function createFrameProcessor(frameProcessor: (frame: Frame) => void): Re * * @worklet * @param frameProcessor The Frame Processor - * @param dependencies The React dependencies which will be copied into the VisionCamera JS-Runtime. * @returns The memoized Frame Processor. * @example * ```ts @@ -36,10 +35,13 @@ export function createFrameProcessor(frameProcessor: (frame: Frame) => void): Re * 'worklet' * const faces = scanFaces(frame) * console.log(`Faces: ${faces}`) - * }, []) + * }) * ``` */ -export function useFrameProcessor(frameProcessor: (frame: Frame) => void, dependencies: DependencyList): ReadonlyFrameProcessor { - // eslint-disable-next-line react-hooks/exhaustive-deps - return useMemo(() => createFrameProcessor(frameProcessor), dependencies) +export function useFrameProcessor(frameProcessor: (frame: Frame) => void): ReadonlyFrameProcessor { + return useMemo( + () => createFrameProcessor(frameProcessor), + // eslint-disable-next-line react-hooks/exhaustive-deps + WorkletsProxy.getWorkletDependencies(frameProcessor), + ) } diff --git a/package/src/skia/useSkiaFrameProcessor.ts b/package/src/skia/useSkiaFrameProcessor.ts index 30efc014ca..32af5c08ca 100644 --- a/package/src/skia/useSkiaFrameProcessor.ts +++ b/package/src/skia/useSkiaFrameProcessor.ts @@ -1,5 +1,4 @@ import type { Frame, FrameInternal } from '../types/Frame' -import type { DependencyList } from 'react' import { useEffect, useMemo } from 'react' import type { Orientation } from '../types/Orientation' import type { DrawableFrameProcessor } from '../types/CameraProps' @@ -247,7 +246,6 @@ export function createSkiaFrameProcessor( * * @worklet * @param frameProcessor The Frame Processor - * @param dependencies The React dependencies which will be copied into the VisionCamera JS-Runtime. * @returns The memoized Skia Frame Processor. * @example * ```ts @@ -260,13 +258,10 @@ export function createSkiaFrameProcessor( * const rect = Skia.XYWHRect(face.x, face.y, face.width, face.height) * frame.drawRect(rect) * } - * }, []) + * }) * ``` */ -export function useSkiaFrameProcessor( - frameProcessor: (frame: DrawableFrame) => void, - dependencies: DependencyList, -): DrawableFrameProcessor { +export function useSkiaFrameProcessor(frameProcessor: (frame: DrawableFrame) => void): DrawableFrameProcessor { const surface = WorkletsProxy.useSharedValue({}) const offscreenTextures = WorkletsProxy.useSharedValue([]) @@ -292,6 +287,6 @@ export function useSkiaFrameProcessor( return useMemo( () => createSkiaFrameProcessor(frameProcessor, surface, offscreenTextures), // eslint-disable-next-line react-hooks/exhaustive-deps - dependencies, + WorkletsProxy.getWorkletDependencies(frameProcessor), ) } diff --git a/package/src/types/CameraProps.ts b/package/src/types/CameraProps.ts index ea96a6ce76..01850a9640 100644 --- a/package/src/types/CameraProps.ts +++ b/package/src/types/CameraProps.ts @@ -314,7 +314,7 @@ export interface CameraProps extends ViewProps { * 'worklet' * const faces = scanFaces(frame) * console.log(`Faces: ${faces}`) - * }, []) + * }) * * return * ``` diff --git a/package/src/types/Frame.ts b/package/src/types/Frame.ts index f558c51bf3..cf11c01ebf 100644 --- a/package/src/types/Frame.ts +++ b/package/src/types/Frame.ts @@ -10,7 +10,7 @@ import type { PixelFormat } from './PixelFormat' * const frameProcessor = useFrameProcessor((frame) => { * 'worklet' * console.log(`Frame: ${frame.width}x${frame.height} (${frame.pixelFormat})`) - * }, []) + * }) * ``` */ export interface Frame { @@ -73,7 +73,7 @@ export interface Frame { * const data = new Uint8Array(buffer) * console.log(`Pixel at 0,0: RGB(${data[0]}, ${data[1]}, ${data[2]})`) * } - * }, []) + * }) * ``` */ toArrayBuffer(): ArrayBuffer diff --git a/package/yarn.lock b/package/yarn.lock index 39fcc4d245..8067b5b317 100644 --- a/package/yarn.lock +++ b/package/yarn.lock @@ -6744,10 +6744,10 @@ react-native-reanimated@^3.8.1: convert-source-map "^2.0.0" invariant "^2.2.4" -react-native-worklets-core@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/react-native-worklets-core/-/react-native-worklets-core-1.2.0.tgz#155040a48d1e3f66107544867ec579c2f5044271" - integrity sha512-Uuf1Hg4V1w4I8eYNinwnnf+h+2YdQgwkUDQszY2yEEXWPWcZBWec0xjYn079RFRcKmGWuJ/zOtokjwy/IjJ9Fw== +react-native-worklets-core@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/react-native-worklets-core/-/react-native-worklets-core-1.3.2.tgz#0851ad7b524eae152d63adc20ad626eabecd9f26" + integrity sha512-wx2MdfiUP9gBluhtzrb5XeiAGxdnqVqosLCPIhP1sSF9EcgOKTBV4jxuRkYmZJ/DDcHvGPy0qwdqwyVtWAn3jQ== dependencies: string-hash-64 "^1.0.3"