Barcode scanner causes app to freeze when opened multiple times

It is mainly based on this stale post.

Basically I have components like this based on the snack example of barcodeScanner:

export default class Scanner extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            hasCameraPermission: false,
            lastScan: null
        };
    }
    componentDidMount() {
        this._requestCameraPermission();
    }
    
    _requestCameraPermission = async () => {
        const { status } = await Permissions.askAsync(Permissions.CAMERA);
        this.setState({
            hasCameraPermission: status === 'granted',
        });
    };

    /**
     * Callback to be called by BarcodeScanner once it reads a compatible barcode, with param destructured
     * @param {Object} result: The result will be in the form of {type:String, data:String}
     */
    onSuccessRead = ({ type, data }) => {
        if (data !== this.state.lastScan) {
            this.setState({ lastScan: data })

            //Get a displayable version of the passed barcode type
            switch (type) {
                case BarCodeScanner.Constants.BarCodeType.aztec: type = "Aztec"; break;
                case BarCodeScanner.Constants.BarCodeType.codabar: type = "Codabar"; break;
                case BarCodeScanner.Constants.BarCodeType.code128: type = "Code128"; break;
                case BarCodeScanner.Constants.BarCodeType.code138: type = "Code138"; break;
                case BarCodeScanner.Constants.BarCodeType.code39: type = "Code39"; break;
                case BarCodeScanner.Constants.BarCodeType.code39mod43: type = "Code39mod43"; break;
                case BarCodeScanner.Constants.BarCodeType.code93: type = "Code93"; break;
                case BarCodeScanner.Constants.BarCodeType.datamatrix: type = "Datamatrix"; break;
                case BarCodeScanner.Constants.BarCodeType.ean13: type = "Ean13"; break;
                case BarCodeScanner.Constants.BarCodeType.ean8: type = "Ean8"; break;
                case BarCodeScanner.Constants.BarCodeType.interleaved2of5: type = "Interleaved2of5"; break;
                case BarCodeScanner.Constants.BarCodeType.itf14: type = "Itf14"; break;
                case BarCodeScanner.Constants.BarCodeType.maxicode: type = "Maxicode"; break;
                case BarCodeScanner.Constants.BarCodeType.pdf417: type = "Pdf417"; break;
                case BarCodeScanner.Constants.BarCodeType.qr: type = "QRCode"; break;
                case BarCodeScanner.Constants.BarCodeType.rss14: type = "Rss14"; break;
                case BarCodeScanner.Constants.BarCodeType.rssexpanded: type = "Rssexpanded"; break;
                case BarCodeScanner.Constants.BarCodeType.upc_a: type = "Upc_a"; break;
                case BarCodeScanner.Constants.BarCodeType.upc_e: type = "Upc_e"; break;
                case BarCodeScanner.Constants.BarCodeType.upc_ean: type = "Upc_ean"; break;
            }

            this.props.onRead(type, data, () => { this.setState({ lastScan: null }) }); //callback, what to do with the data on success read
        }
    }

    render() {
        return (
            <View style={styles.container}>
                {
                    this.state.hasCameraPermission === null ?
                        <Text>Requesting for camera permission</Text> :
                        this.state.hasCameraPermission === false ?
                            <Text style={{ color: '#fff' }}> Camera permission is not granted </Text> :

                            <BarCodeScanner
                                onBarCodeRead={this.onSuccessRead.bind(this)}
                                style={styles.scanArea}
                                autoFocus={Camera.Constants.AutoFocus.on}
                                focusDepth={1}
                                torchMode={this.props.torch}>

                                <View style={styles.layerTop} />
                                <View style={styles.layerCenter}>
                                    <View style={styles.layerLeft} />
                                    <View style={styles.focused} />
                                    <View style={styles.layerRight} />
                                </View>
                                <View style={styles.layerBottom} />
                            </BarCodeScanner>
                }
            </View>
        );
    }
}

Then this scanner will be called by 2 different interfaces (containers) depending on user’s choice.
Both containers are very similar and look something like:

class Link extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      torchOn: false,
      scanOn: true
    }

    this.onBarcodeRead = this.onBarcodeRead.bind(this);
  }

  componentDidUpdate() {
    if (this.state.scanOn !== this.props.scanOn)
      this.setState({ scanOn: this.props.scanOn })
  }

  onBarcodeRead(type, data, onCancel) {
    Alert.alert(
      'A ' + type + ' has been found',
      'Content:\n' + data,
      [{ text: 'Open URL', onPress: () => Linking.openURL(data) },
      { text: 'Cancel', onPress: onCancel() }]
    );
  }

  render() {
    return (
      <View style={styles.container}>
        {this.state.scanOn ?
          <Scanner
            onRead={this.onBarcodeRead}
            torch={this.state.torchOn ? 'on' : 'off'} />
          : null
        }
        <View style={styles.backButtonContainer}>
          <TouchableOpacity style={{ flex: 1 }} onPress={() => { this.setState({ scanOn: false }); Actions.pop(); }}>
            <View style={styles.backButton}>
              <Icon name='action-undo' type='simple-line-icon' size={40} color='white' />
            </View>
          </TouchableOpacity>
        </View>
        <View style={styles.torchButtonContainer}>
          <TouchableOpacity style={{ flex: 1 }} onPress={() => this.setState({ torchOn: !this.state.torchOn })}>
            <View style={styles.torchButton}>
              {
                this.state.torchOn ?
                  <Icon name='flashlight-off' type='material-community' size={40} color='white' />
                  :
                  <Icon name='flashlight' type='material-community' size={40} color='white' />
              }
            </View>
          </TouchableOpacity>
        </View>
      </View>
    );
  }
}

export default Link;

As you can see I have tried unmounting the scanner component everytime I’m leaving one of these interfaces. The interfaces are connected through RNRF tabs, that’s why I have to manually unmount the scanner through the scanOn state on back button press.

This mechanism is problematic only when I keep going back and forth between interface A and B which uses this scanner component. It will cause the app to freeze at some point depending on the phone capacity.

is there some configurations that I’m missing here or is this a bug?
since I’ve tested that componentWillUnmount() was called from my Scanner component, I assume that the unmounting mechanism was successful and my only guess of why would this mechanism cause the app to freeze is that there are some async back-end operation that’s still running and keep getting piled up before ever terminated. Is there any testing that I can do to find out what it is?

Any pointers would be appreciated here, thanks in advance :slight_smile:

This topic was automatically closed 15 days after the last reply. New replies are no longer allowed.