From bb51c06d488e11abee51bd0c4129ce88b4063591 Mon Sep 17 00:00:00 2001 From: Ray <41517811+rayabelcode@users.noreply.github.com> Date: Fri, 25 Sep 2026 13:11:35 -0400 Subject: [PATCH] fix: Don't capture CameraImpl's scope in the PreviewView hybridRef callback The hybridRef callback passed to NativePreviewView was created inside CameraImpl. Native keeps that callback for the lifetime of the native view, and on Hermes its environment chain includes CameraImpl's scope, so the component's props, and every app callback passed to , stayed reachable after unmount. Create the callback in a module-level factory that captures only the ref object. Behavior is unchanged. Relates to #4180 --- .../src/views/Camera.tsx | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/react-native-vision-camera/src/views/Camera.tsx b/packages/react-native-vision-camera/src/views/Camera.tsx index 2ad612d80e..fd0429f831 100644 --- a/packages/react-native-vision-camera/src/views/Camera.tsx +++ b/packages/react-native-vision-camera/src/views/Camera.tsx @@ -1,4 +1,10 @@ -import React, { type Ref, useImperativeHandle, useMemo, useRef } from 'react' +import React, { + type Ref, + type RefObject, + useImperativeHandle, + useMemo, + useRef, +} from 'react' import type { ViewProps } from 'react-native' import { callback } from 'react-native-nitro-modules' import type { SharedValue } from 'react-native-reanimated' @@ -109,6 +115,15 @@ export interface CameraViewProps ref?: Ref } +// Module-level so the callback handed to native captures only the ref object. +// Native holds this callback for as long as the native view lives; created inside +// CameraImpl it would keep CameraImpl's whole scope alive, including its props. +function createHybridRefSetter(ref: RefObject) { + return callback((r: PreviewView) => { + ref.current = r + }) +} + function CameraImpl({ implementationMode, resizeMode, @@ -131,13 +146,7 @@ function CameraImpl({ // 4. Create `ref` for `PreviewView` const previewViewRef = useRef(null) - const setHybridRef = useMemo( - () => - callback((r: PreviewView) => { - previewViewRef.current = r - }), - [], - ) + const setHybridRef = useMemo(() => createHybridRefSetter(previewViewRef), []) // 5. Create a ref that exposes some funcs on the Controller and the PreviewView. useImperativeHandle(