barcode-reader-javascript icon indicating copy to clipboard operation
barcode-reader-javascript copied to clipboard

Illegal constructor

Open regisoc opened this issue 1 year ago • 7 comments
trafficstars

Hello,

Short version: maybe I am just blind after some hours spent on that. There is an annoying "Illegal constructor" appearing and I cannot figure out why.

I am trying to set up the React VideoCapture element as mentioned here.

It looks like this:

`VideoCapture` element
import React from 'react';

import '../dynamsoft.config';

// --Change starts
// For some reason, import from Dynamsoft does not work. But the library is creating a global
// `Dynamsoft` object that has all the library contents.

// @ts-ignore
import "dynamsoft-camera-enhancer";
// @ts-ignore
import "dynamsoft-capture-vision-router";
// @ts-ignore
import "dynamsoft-utility";

const Dynamsoft = (window as any).Dynamsoft;
const CameraEnhancer = Dynamsoft.DCE.CameraEnhancer;
const CameraView = Dynamsoft.DCE.CameraView;
const CaptureVisionRouter = Dynamsoft.CVR.CaptureVisionRouter;
const MultiFrameResultCrossFilter = Dynamsoft.Utility.MultiFrameResultCrossFilter;

// -- Change end

const componentDestroyedErrorMsg = "VideoCapture Component Destroyed";

class VideoCapture extends React.Component<{ onSubmit: Function }, {}> {
  cameraViewContainer: React.RefObject<HTMLDivElement> = React.createRef();

  resolveInit?: () => void;
  pInit: Promise<void> = new Promise((r) => (this.resolveInit = r));
  isDestroyed = false;

  cvRouter?: typeof Dynamsoft.CVR;
  cameraEnhancer?: typeof Dynamsoft.CE;

  async componentDidMount() {
    try {
      // Create a `CameraEnhancer` instance for camera control and a `CameraView` instance for UI control.
      const cameraView = await CameraView.createInstance();
      if (this.isDestroyed) {
        throw Error(componentDestroyedErrorMsg);
      } // Check if component is destroyed after every async

      this.cameraEnhancer = await CameraEnhancer.createInstance(cameraView);
      if (this.isDestroyed) {
        throw Error(componentDestroyedErrorMsg);
      }

      // Get default UI and append it to DOM.
      this.cameraViewContainer.current!.append(cameraView.getUIElement());

      // Create a `CaptureVisionRouter` instance and set `CameraEnhancer` instance as its image source.
      this.cvRouter = await CaptureVisionRouter.createInstance();
      if (this.isDestroyed) {
        throw Error(componentDestroyedErrorMsg);
      }
      this.cvRouter.setInput(this.cameraEnhancer);

      // Define a callback for results.
      this.cvRouter.addResultReceiver({
        onDecodedBarcodesReceived: (result: any) => {
          if (!result.barcodeResultItems.length) return;

          for (let item of result.barcodeResultItems) {
            this.props.onSubmit(item.text);
          }
        },
      });

      // Filter out unchecked and duplicate results.
      const filter = new MultiFrameResultCrossFilter();
      // Filter out unchecked barcodes.
      filter.enableResultCrossVerification("barcode", true);
      // Filter out duplicate barcodes within 3 seconds.
      filter.enableResultDeduplication("barcode", true);
      await this.cvRouter.addResultFilter(filter);
      if (this.isDestroyed) {
        throw Error(componentDestroyedErrorMsg);
      }

      // Open camera and start scanning single barcode.
      await this.cameraEnhancer.open();
      if (this.isDestroyed) {
        throw Error(componentDestroyedErrorMsg);
      }
      await this.cvRouter.startCapturing("ReadSingleBarcode");
      if (this.isDestroyed) {
        throw Error(componentDestroyedErrorMsg);
      }
    } catch (ex: any) {
      if ((ex as Error)?.message === componentDestroyedErrorMsg) {
        console.log(componentDestroyedErrorMsg);
      } else {
        let errMsg = ex.message || ex;
        console.error(errMsg);
        console.log("ERROR HERE");
        console.log(ex);
        alert(errMsg);
      }
    }

    // Resolve pInit promise once initialization is complete.
    this.resolveInit!();
  }

  async componentWillUnmount() {
    this.isDestroyed = true;
    try {
      // Wait for the pInit to complete before disposing resources.
      await this.pInit;
      this.cvRouter?.dispose();
      this.cameraEnhancer?.dispose();
    } catch (_) {}
  }

  shouldComponentUpdate() {
    // Never update UI after mount, sdk use native way to bind event, update will remove it.
    return false;
  }

  render() {
    return (
      <div ref={this.cameraViewContainer} style={{  width: "100%", height: "70vh" }}></div>
    );
  }
}

export default VideoCapture;

The VideoCapture component is embedded is another QRCodeReader element.

`QRCodeReader` element
import { useState } from 'react';
import VideoCapture from "./VideoCapture";

const QRCodeReader = (props) => {
    const [errors, setErrors] = useState(null);

    const onSubmit = async (param) => {
        try {
            const isUpdated = await props.onSubmit(param);
            if (isUpdated) {
                setErrors(null);
            }
        } catch(error){
            setErrors(error);
        }
    }

    return (
        <div>
            <VideoCapture onSubmit={onSubmit} />
            {errors}
        </div>
    );
}

export default QRCodeReader;

Until here, everything works well. Using the QRCodeReader element, it can scan QRCode and extract the item elements. Nice.

The issue happens after that, when I try to embed the QRCodeReader element into a QRModal Element. Elements looks like this VideoCapture <- QRCodeReader <- QRModal.

We want to use them in a process that is the following:

  • first scan: uses the QRCodeReader (full page reader).
  • updates the page to match info in the QRCode, loads a list of items from db on the page, each item has a scan modal button to trigger the QRModal, to access the detail of each item, we must scan again using the modal.
  • second scan: uses the QRModal (modal reader) -> fails consistently with the same error.
`QRModal` element
import React, {useEffect, useState} from 'react';
import PropTypes from 'prop-types';
import Modal from 'Modal';
import QRCodeReader from './QRCodeReader';

/**
 * Widget to access a profile using a QR code
 *
 * @param {object} props - React props
 *
 * @return {ReactDOM}
 */
const QRModal = (props) => {
  const [code, setCode] = useState(null);

  useEffect(() => {
  }, []);

  return (
    <Modal
      onClose={() => {
        props.onClose();
      }}
      show={true}
      title={props.modalTitle}
    >
      <QRCodeReader onSubmit={(c) => {
        if (c === code) return;
        setCode(c);
        props.onScan(c);
      }} />
    </Modal>
  );
}

QRModal.defaultProps = {
  modalTitle: 'Scan QR Code',
};

QRModal.propTypes = {
  modalTitle: PropTypes.string,
};

export default QRModal;

This is throwing a Type Error: Illegal constructor. apparently coming from CameraView.createInstance().

I also tried to directly shortcut QRModal to directly call another VideoCapture class (VideoCapture2 is a copy of VideoCapture). Same result.

image

regisoc avatar Nov 15 '24 15:11 regisoc