Search code examples
javascriptreact-nativeqr-codereact-native-vision-camera

App crashes on API call in frameProcessor React Native


I am using a dependency called vision-camera-code-scanner for QR code scanning in my React Native app. I am getting QR code scan data properly. But i need to pass that data to make an API call. But when i try to do that, it crashes the application. Not sure what should i do here.

Here's my component:

import React, { useState, useCallback, useEffect, useMemo } from "react";
import { StyleSheet, Text } from "react-native";
import {
  Camera,
  useCameraDevices,
  useFrameProcessor,
} from "react-native-vision-camera";
import { useDispatch, useSelector } from "react-redux";
import * as appActions from "../../../redux/app/app.actions";
import { BarcodeFormat, scanBarcodes } from "vision-camera-code-scanner";

interface ScanScreenProps {}

const Scan: React.FC<ScanScreenProps> = () => {
  const [hasPermission, setHasPermission] = useState(false);
  const devices = useCameraDevices();
  const device = devices.back;
  const dispatch = useDispatch();
  const validateQRStatus = useSelector(validationQRSelector);

  const frameProcessor = useFrameProcessor((frame) => {
    "worklet";
    const detectedBarcodes = scanBarcodes(frame, [BarcodeFormat.QR_CODE], {
      checkInverted: true,
    });
    if (detectedBarcodes?.length !== 0) {
        const resultObj = JSON.parse(detectedBarcodes[0].rawValue);
        const paramData = `token:${Object.values(resultObj)[0]}`;
        validate(paramData);
  }, []);

  const validate = useCallback((param: string) => dispatch(appActions.validateQR(param)));

  useEffect(() => {
    (async () => {
      const status = await Camera.requestCameraPermission();
      setHasPermission(status === "authorized");
    })();
  }, []);

  return (
    device != null &&
    hasPermission && (
      <>
        <Camera
          style={StyleSheet.absoluteFill}
          device={device}
          isActive={true}
          frameProcessor={frameProcessor}
          frameProcessorFps={5}
        />
        {/* {barcodes.map((barcode, idx) => (
          <Text key={idx} style={styles.barcodeTextURL}>
            {barcode.barcodeFormat + ": " + barcode.barcodeText}
          </Text>
        ))} */}
        <Text style={styles.barcodeTextURL}>camera</Text>
      </>
    )
  );
};

export default Scan;

const styles = StyleSheet.create({
  barcodeTextURL: {
    fontSize: 20,
    color: "white",
    fontWeight: "bold",
    alignSelf: "center",
  },
});


Solution

  • Your problem is that a worklet is run in a separate JS thread. If you need to call any function from your main thread you need to use runOnJS (https://docs.swmansion.com/react-native-reanimated/docs/next/api/miscellaneous/runOnJS/)

    
    import { runOnJS } from 'react-native-reanimated';
    
      const frameProcessor = useFrameProcessor((frame) => {
        "worklet";
        const detectedBarcodes = scanBarcodes(frame, [BarcodeFormat.QR_CODE], {
          checkInverted: true,
        });
        if (detectedBarcodes?.length !== 0) {
            const resultObj = JSON.parse(detectedBarcodes[0].rawValue);
            const paramData = `token:${Object.values(resultObj)[0]}`;
            runOnJS(validate)(paramData);
      }, []);