3

I have a simple expo managed React Native project setup with react-navigation I have 2 screens one of which is the Home screen and the other is a screen for QRCode scanner which uses expo's BarCodeScanner.

The issue here is that when navigating from the Home screen to the QRCode screen and back to the Home screen keeps the camera alive. I can see the activity in the status bar saying 'Expo Go is using your camera'

I tried various ways to tackle this,

  • Passing the screen as a render prop to Stack.Screen so it mounts every time we navigate, but still the same issue

  • Tried using the isFocused method to conditionally render the component but still no luck

     <NavigationContainer fallback={<Text>Loading...</Text>}>
       <Stack.Navigator
         screenOptions={({ route, navigation }) => ({
           headerShadowVisible: false,
           headerTitle: () => (
             <Text
               style={{
                 fontSize: 30,
                 fontFamily: Font["900"],
               }}
             >
               Test
             </Text>
           ),
           headerRight: () =>
             route.name !== "QR" ? (
               <Pressable onPress={() => navigation.navigate("QR")}>
                 <QrcodeIcon size={26} color="black" />
               </Pressable>
             ) : null,
         })}
       >
         <Stack.Screen name="Home" component={Home} />
         <Stack.Screen name="QR" children={() => <QRCode />} />
       </Stack.Navigator>
     </NavigationContainer>
    

And the code for the QRCode component looks like the following:

const QRCode = () => {
  const [hasPermission, setHasPermission] = useState<boolean>();
  const [scanned, setScanned] = useState<boolean>(false);
  const isFocused = useIsFocused();
  const linkTo = useLinkTo();

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

  const handleBarCodeScanned = ({ type, data }: BarCodeEvent) => {
    setScanned(true);
    linkTo(data);
  };

  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.absoluteFill}
        />
      ) : null}
    </View>
  );
};

1 Answers1

0

This issue comes from React Navigation caching it's screens (and providing poor cache invalidation apis). Switching from nagitation.navigate to navigation.reset solved the issue for me:

navigation.reset({
    index: 1,
    routes: [
      {
        name: 'ScreenYouWantToNavigateTo',
      },
    ],
  })

Keep in mind that this will reset your navigation stack. Another solution would be to use the isFocused hook, see this thread for more.