react-native-vision-camera
Version:
VisionCamera is the fastest and most powerful Camera for react-native.
87 lines (86 loc) • 3.83 kB
JavaScript
import { useEffect, useMemo, useState } from 'react';
import { useMemoizedArray } from './useMemoizedArray';
import { useStableCallback } from './useStableCallback';
/**
* Connect the given {@linkcode device} to the given {@linkcode outputs}
* with the given {@linkcode mirrorMode} applied to each output,
* and return a {@linkcode CameraController}.
*
* @note The {@linkcode outputs} have to be explicitly memoized.
*/
export function useCameraController(session, device, outputs, { mirrorMode = 'auto', constraints = [], onSessionConfigSelected, allowBackgroundAudioPlayback, getInitialExposureBias, onConfigured, getInitialZoom, } = {}) {
const [controller, setController] = useState();
// Keep some things stable so the `useEffect` doesn't re-trigger
const stableOutputs = useMemoizedArray(outputs);
const stableGetInitialExposureBias = useStableCallback(getInitialExposureBias ?? (() => undefined));
const stableGetInitialZoom = useStableCallback(getInitialZoom ?? (() => undefined));
const stableOnConfigured = useStableCallback(onConfigured ?? (() => { }));
const stableOnSessionConfigSelected = useStableCallback(onSessionConfigSelected ?? (() => { }));
// TODO: Can we use something like useSyncExternalStore or whatever to avoid "wrong" dependencies?
// biome-ignore lint/correctness/useExhaustiveDependencies: It's an array of objects, we either have to deep-memo or just stringify.
const stableConstraints = useMemo(() => {
return [
...constraints,
...stableOutputs.map((o) => ({ resolutionBias: o })),
];
}, [JSON.stringify(constraints), stableOutputs]);
// This effect re-configures the CameraSession and returns a `controller`.
// This is expensive and should only be done if any inputs change.
// To prevent shallow inequality causing a re-configuration,
// we memoize all objects, callbacks or arrays beforehand.
useEffect(() => {
if (session == null) {
// default: null -> empty controllers
setController(undefined);
return;
}
let isCanceled = false;
const load = async () => {
if (device == null) {
// No device, configure with empty devices
session.configure([], {});
setController(undefined);
}
else {
// Device + outputs - configure session
const controllers = await session.configure([
{
input: device,
outputs: stableOutputs.map((o) => ({
output: o,
mirrorMode: mirrorMode,
})),
constraints: stableConstraints,
initialExposureBias: stableGetInitialExposureBias?.(),
initialZoom: stableGetInitialZoom?.(),
onSessionConfigSelected: stableOnSessionConfigSelected,
},
], { allowBackgroundAudioPlayback: allowBackgroundAudioPlayback });
if (isCanceled) {
controllers.forEach((c) => {
c.dispose();
});
return;
}
stableOnConfigured?.();
setController(controllers[0]);
}
};
load();
return () => {
isCanceled = true;
};
}, [
device,
mirrorMode,
session,
allowBackgroundAudioPlayback,
stableOutputs,
stableOnConfigured,
stableGetInitialExposureBias,
stableGetInitialZoom,
stableOnSessionConfigSelected,
stableConstraints,
]);
return controller;
}