12

Because of Google Play, I had to update an old project of mine to the latest expo versions (version 43.0.0 to be exact). The idea is for the app to scan a QRCode and process the data, simply. However, expo-barcode-scanner only works once and after that I need to close and open the app again to work. Has anyone encountered this problem and (or) knows how to solve it? Below is my code:

{escaneando ? (
                        <BarCodeScanner
                            barCodeTypes={[
                                BarCodeScanner.Constants.BarCodeType.ean13,
                                BarCodeScanner.Constants.BarCodeType.ean8,
                                BarCodeScanner.Constants.BarCodeType.upc_a,
                                BarCodeScanner.Constants.BarCodeType.upc_e,
                            ]}
                            onBarCodeScanned={this.handleBarCode.bind(this)}
                            style={[StyleSheet.absoluteFillObject, styles.barscan]}
                        />
                    ) : null}

And library specifications:

"@react-native-community/masked-view": "^0.1.11",
    "@react-native-community/netinfo": "^6.0.5",
    "@react-navigation/native": "^6.0.6",
    "@react-navigation/stack": "^6.0.11",
    "expo": "~43.0.0",
    "expo-av": "^10.1.3",
    "expo-barcode-scanner": "^11.1.2",
    "expo-status-bar": "~1.1.0",
    "lodash": "^4.17.21",
    "react": "17.0.1",
    "react-dom": "17.0.1",
    "react-native": "^0.64.2",
    "react-native-dropdownalert": "^4.3.0",
    "react-native-elements": "^3.4.2",
    "react-native-gesture-handler": "^1.10.3",
    "react-native-in-app-notification": "^3.2.0",
    "react-native-offline": "^6.0.0",
    "react-native-paper": "^4.10.0",
    "react-native-reanimated": "^2.2.3",
    "react-native-safe-area-context": "^3.3.2",
    "react-native-screens": "^3.9.0",
    "react-native-web": "0.17.1",
    "react-navigation": "^4.4.4",
    "react-redux": "^7.2.6",
    "redux": "^4.1.2",
    "redux-thunk": "^2.4.0",
    "reselect": "^4.1.2"
Backup Gov18
  • 123
  • 1
  • 4
  • You can also use `expo-camera` instead of `expo-barcode-scanner`. `expo-camera` does not have this issue. It also offers more options like flashlight/torch and switching cameras. – ofundefined Nov 12 '21 at 21:13
  • 1
    The issue is partially solved in expo `44.0.1` but it seems on Android it is still not working (me included). Link to follow https://github.com/expo/expo/issues/15733 – Nadarian Dec 30 '21 at 21:29

5 Answers5

7

with expo-camera it works perfectly.

1)- install expo-camera : expo install expo-camera

this is the code for it :

import React, { useState, useEffect } from 'react';
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';
import { Camera } from 'expo-camera';

const QrcodeReader = ({navigation}) =>  {

  const [hasPermission, setHasPermission] = useState(null);
  const [type, setType] = useState(Camera.Constants.Type.back);

  useEffect(() => {
    (async () => {
      const { status } = await Camera.requestCameraPermissionsAsync();
      setHasPermission(status === 'granted');
    })();
  }, []);

  if (hasPermission === null) {
    return <View />;
  }
  if (hasPermission === false) {
    return <Text>No access to camera</Text>;
  }
  return(
  <View style={styles.container}>
      <Camera
        onBarCodeScanned={(...args) => {
          const data = args[0].data;
          result = JSON.stringify(result);
          console.log(result);
          navigation.navigate('your_next_screen',{result});
          );
        }}
        barCodeScannerSettings={{
          barCodeTypes: ['qr'],
        }}
        style={{ flex: 1 }}
      />
    </View>
  );
}
  
const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  camera: {
    flex: 1,
  },
  buttonContainer: {
    flex: 1,
    backgroundColor: 'transparent',
    flexDirection: 'row',
    margin: 20,
  },
  button: {
    flex: 0.1,
    alignSelf: 'flex-end',
    alignItems: 'center',
  },
  text: {
    fontSize: 18,
    color: 'white',
  },
});

export default QrcodeReader;
chikabala
  • 653
  • 6
  • 24
  • 3
    This works well for me as well, not completely sure why expo-barcode-scanner exists when expo-camera provides barcode scanning. – trepidacious Dec 27 '21 at 17:54
  • 1
    Can confirm that this solves the issue. Initially, I tried conditionally rendering BarcodeScanner component depending on the app's focus state using useIsFocused hook as described in the solution below. But to no avail. Then I tried using expo-camera and worked on first try. – adkalkan Aug 23 '22 at 12:22
  • not working for me. Property 'back' does not exist on type 'CameraType'.ts(2339). Tried running yarn add @types/expo-camera and it doesnt exist. Anyone knows a fix? – Athos Franco Jul 04 '23 at 18:45
6

I faced the same problem and because it was a feature i couldnt work without, i persisted and found a solution that worked for me.

Because Only one active BarCodeScanner preview is supported, I went to react navigation and found a way to re-render the barcode scanner whenever the screen is in focus.

import { useIsFocused } from '@react-navigation/native'; useIsFocused returns true when a screen with the barcode scanner is the one in focus.

 {isFocused ? (
    <BarCodeScanner
      onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
      style={StyleSheet.absoluteFillObject}
    />) : null}

Read more on https://reactnavigation.org/docs/use-is-focused.

Full Example:

import React, { useState, useEffect } from 'react';
import { Text, View, StyleSheet, Button } from 'react-native';
import { BarCodeScanner } from 'expo-barcode-scanner';
import { useIsFocused } from '@react-navigation/native';

export default function App() {
  const [hasPermission, setHasPermission] = useState(null);
  const [scanned, setScanned] = useState(false);

  console.log(hasPermission, scanned);
  const isFocused = useIsFocused();

  useEffect(() => {
    (async () => {
      setScanned(false);
      const { status } = await BarCodeScanner.requestPermissionsAsync();
      setHasPermission(status === 'granted');
    })();
  }, []);

  const handleBarCodeScanned = ({ type, data }) => {
    setScanned(true);
    alert(`Bar code with type ${type} and data ${data} has been scanned!`);
  };

  if (hasPermission === null) {
    return <Text>Requesting for camera permission</Text>;
  }
  if (hasPermission === false) {
    return <Text>No access to camera</Text>;
  }

  return (
    <View style={styles.container}>
      {isFocused ? (
        <BarCodeScanner
          onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
          style={StyleSheet.absoluteFillObject}
        />
      ) : null}
      {scanned && (
        <Button title={'Tap to Scan Again'} onPress={() => setScanned(false)} />
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column',
    justifyContent: 'center',
  },
});
nkangi jafari
  • 83
  • 1
  • 4
3

Welcome @Backup Gov18,

This is a documented issue.

Note: Only one active BarCodeScanner preview is supported currently. When using navigation, the best practice is to unmount any previously rendered BarCodeScanner component so the following screens can use without issues.

There is a workaround.

Instead of conditionally rendering the <BarcodeScanner /> component, you could render it inside another dedicated screen component.

This way, after this new screen reads the barcode, you could navigate back to your first screen. Navigating back may unmount this new screen. You can force unmount if you need to.

As you are using react-navigation, you had better use .pop() instead of goBack().


Alternative

You can also use expo-camera instead of expo-barcode-scanner. expo-camera does not have this issue. It also offers more options like flashlight/torch and switching cameras.

ofundefined
  • 2,692
  • 2
  • 18
  • 35
0

I have resolved the issue by forcing updates on the <Camera/> component:

import {useInterval} from 'usehooks-ts';
import {Camera} from 'expo-camera';
import React, {useRef} from 'react';

const QrScanner = () => {
  const cameraRef = useRef<Camera>(null);
  useInterval(() => {
    ref.current && ref.current.forceUpdate();
  }, 1000);

  <Camera
    onBarCodeScanned={(data) => console.log()}
    ref={cameraRef}
  />
}
Daruda
  • 167
  • 3
-1

found this workaround:

edit the file BarCodeScannerViewFinder.kt located at: [your project]\node_modules\expo-barcode-scanner\android\src\main\java\expo\modules\barcodescanner add the following two lines at the declarations part (around line 44):

@volatile private var barCodeScannerTaskLock = false

worked fine for me.

Moshe Dolev
  • 194
  • 2
  • 4