javascriptandroidreactjsreact-nativereact-native-camera

How to scan one barcode per time? [react-native-camera]


Actually i'm new to React and i'm trying to make a simple barcode scanner which show the scanned barcode in an alert and after pressing "OK" in the alert the user should be able to scan another barcode.

The issue is that the barcode is continuously scanned and when the alert is up it's hiding and showing every second the alert.

I was trying to do something like this to show the alert only once and if OK is pressed to be able to show again the alert but only in case the OK is pressed but that had no effect..

  onBarCodeRead = (e) => {
    if(!this.alertPresent){
      this.alertPresent = true;
          Alert.alert(
            "Barcode type is " + e.type,
            "Barcode value is " + e.data,
            [
                 {text: 'OK', onPress: () => this.alertPresent = false;},
            ],
              {cancelable: false},
          );
      }
  }

Here is full code of Barcode.JS

import React, { Component } from 'react';
import { Button, Text, View,Alert } from 'react-native';
import { RNCamera } from 'react-native-camera';
import BarcodeMask from 'react-native-barcode-mask';
class ProductScanRNCamera extends Component {

  constructor(props) {
    super(props);
    this.camera = null;
    this.barcodeCodes = [];
    this.alertPresent = false;
    this.state = {
      camera: {
        flashMode: RNCamera.Constants.FlashMode.auto,
      }
    };
  }

  onBarCodeRead = (e) => {
    if(!this.alertPresent){
      this.alertPresent = true;
          Alert.alert(
            "Barcode type is " + e.type,
            "Barcode value is " + e.data,
            [
                 {text: 'OK', onPress: () => this.alertPresent = false;},
            ],
              {cancelable: false},
          );
      }
  }


  pendingView() {
    return (
      <View
      style={{
        flex: 1,
        backgroundColor: 'lightgreen',
        justifyContent: 'center',
        alignItems: 'center',
      }}
      >
      <Text>Waiting</Text>
      </View>
    );
  }

  render() {


    return (
      <View style={styles.container}>
      <RNCamera
      ref={ref => {
        this.camera = ref;
      }}
      defaultTouchToFocus
      flashMode={this.state.camera.flashMode}
      mirrorImage={false}
      onBarCodeRead={this.onBarCodeRead.bind(this)}
      onFocusChanged={() => {}}
      onZoomChanged={() => {}}
      style={styles.preview}
      >
      <BarcodeMask/>
      </RNCamera>
      </View>
    );
  }
}

Solution

  • The trick here is to modify barcodeTypes props with an internal state.

    const defaultBarcodeTypes = [// should be all Types from RNCamera.Constants.BarCodeType];
    
    class ProductScanRNCamera extends Component {
       state = {
          // your other states
          barcodeType: '',
          barcodeValue: '',
          isBarcodeRead: false // default to false
       }
    
       onBarcodeRead(event) {
          this.setState({isBarcodeRead: true, barcodeType: event.type, barcodeValue: event.data});
       }
    
       // run CDU life-cycle hook and check isBarcodeRead state
       // Alert is a side-effect and should be handled as such.
       componentDidUpdate() {
          const {isBarcodeRead, barcodeType, barcodeValue} = this.state;
          if (isBarcodeRead) {
             Alert.alert(barcodeType, barcodeValue, [
               { 
                   text: 'OK', 
                   onPress: () => {
                       // Reset everything 
                       this.setState({isBarcodeRead: false, barcodeType: '', barcodeValue: ''})
                   }
               }
             ]);
          }
    
       }
    
       render() {
          const {isBarcodeRead} = this.state;
          return (
             <RNCamera {...your other props} barcodeTypes={isBarcodeRead ? [] : defaultBarcodeTypes}>
                <BarcodeMask />
             </RNCamera>
          )
    
       }
    }
    

    A hook version is cleaner

    const ProductScanRNCamera = () => {
       const [isBarcodeRead, setIsBarcodeRead] = useState(false);
       const [barcodeType, setBarcodeType] = useState('');
       const [barcodeValue, setBarcodeValue] = useState('');
    
       useEffect(() => {
          if (isBarcodeRead) {
              Alert.alert(
                 barcodeType, 
                 barcodeValue, 
                 [
                    {
                        text: 'OK',
                        onPress: () => {
                            // reset everything
                            setIsBarcodeRead(false);
                            setBarcodeType('');
                            setBarcodeValue('');
                        }
                    }
                 ]
              );
          }
    
       }, [isBarcodeRead, barcodeType, barcodeValue]);
    
       const onBarcodeRead = event => {
          if (!isBarcodeRead) {
             setIsBarcodeRead(true);
             setBarcodeType(event.type);
             setBarcodeValue(event.data);
          }
       }
    
       return (
          <RNCamera {...your props} 
                    onBarCodeRead={onBarcodeRead} 
                    barcodeTypes={isBarcodeRead ? [] : defaultBarcodeTypes}>
             <BarcodeMask />
          </RNCamera>
       )
    }