Search code examples
reactjsreact-nativeexpo-camera

Return image uri to main react component


I'm adding a camera to an already developed react native project. I want to return the imageUri (set by takePicture in the camera component) to the calling component after the image is confirmed by user input to the image component.

Calling component:

<FAB style={styles.fab} test id="goToCamera" small icon="camera" onPress={() => navigation.navigate("Camera")}></FAB>

Camera component:

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

import { setImageUri, imageUri } from "../screens/ImageValidationScreen";
import colors from "../config/colors";

function CameraScreen({ navigation }) {
    const [hasPermission, setHasPermission] = useState(null);
    const [type, setType] = useState(CameraType.back);
    const [camera, setCamera] = useState(null);
    const [imageUri, setImageUri] = useState(null);
    const [cameraToggle, setCameraToggle] = useState(null);

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

    const takePicture = async () => {
        if (camera) {
          const data = await camera.takePictureAsync(null);
          console.log(data.uri);
          setImageUri(data.uri);          
        }
    };

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

    
    return (
      <View style={styles.container}>
        <Camera style={styles.camera} type={type} ref={(ref)=> setCamera(ref)}>
            <View style={styles.buttonContainer}>`enter code here`
                <TouchableOpacity 
                    style={styles.captureButton} 
                    onPress={() => {
                        takePicture();
                        navigation.navigate("Image");
                    }}>
                </TouchableOpacity> 
                <TouchableOpacity
                    onPress={() => { setType(type === CameraType.back ? CameraType.front : CameraType.back); }}>
                    <Text style={styles.text}> Flip </Text>
                </TouchableOpacity>
            </View>
        </Camera>
      </View>
    );  
}

Image component:

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

import imageUri from "../screens/CameraScreen"
import colors from "../config/colors";

function ImageValidationScreen({ navigation }) {
  return (
      <View>
        <TouchableOpacity 
          onPress={() => {
            navigation.navigate("CSS");
          }}>
            <Text style={styles.text}> Done </Text>
        </TouchableOpacity>
        {imageUri && <Image source={{ uri: imageUri }} />}
      </View>
  );  
}


const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  text: {
    fontSize: 20,
    color: 'black',
  },
});

export default ImageValidationScreen;

Right now, I think the imageUri is correctly retrieved/printed to the console, but is being incorrectly retrieved by the Image component. I'm not sure how to pass this value to the calling component. Thank you so much in advance!!


Solution

  • The best way to do this is by passing parameters to image routes, you can see the documentation here.

    So, you can pass object in navigation.navigate second parameter like this:

    navigation.navigate("Image", {
     imageUri: imageUri
    });
    

    In your case, you must get image URL from takePicture promise and then pass it to navigate method

    const takePicture = async () => {
      if (camera) {
        const data = await camera.takePictureAsync(null);
        console.log(data.uri);
        setImageUri(data.uri);
        return data.uri
      }
    };
    

    and then the onPress event

    onPress={() => {
      (async()=>{
        const imageURI = await takePicture();
        navigation.navigate("Image", {imageUri: imageURI});
      })()
    }}
    

    To access imageURI in image component, you can do this:

    function ImageValidationScreen({ route, navigation }) {
      const { imageUri } = route.params
      return (
          <View>
            <TouchableOpacity 
              onPress={() => {
                navigation.navigate("CSS");
              }}>
                <Text style={styles.text}> Done </Text>
            </TouchableOpacity>
            {imageUri && <Image source={{ uri: imageUri }} />}
          </View>
      );  
    }
    

    Also, just remove import imageUri from "../screens/CameraScreen" since imageUri isn't exported from CameraScreen file.

    Haven't tested this though, let me know if it doesn't work.