Search code examples
javascriptreact-nativeexpouse-refexpo-camera

Expo-Camera, takePictureAsync undefined (Unhandled promise rejection: TypeError: ref.current.takePictureAsync is not a function)


I'm trying to create react app with expo using expo-camera for taking pictures. I have separately components MeasurementCameraScreen and MeasurementCamera. I'm using useRef() hook to be able to call takePictureAsync() from the MeasuremenCameraScreen.

When pressing the take image -button takePicture() console.logs the ref, so I assume the onPress gets there, but then I get the following error message:

[Unhandled promise rejection: TypeError: ref.current.takePictureAsync is not a function. (In 'ref.current.takePictureAsync(options)', 'ref.current.takePictureAsync' is undefined)]

I saw that people have also had same issues with takePictureAcync(), but I haven't found solution to my problem. I also tired to combine the MeasurementCameraScreen and MeasurementCamera components to one component, and with that I got the camera working, but I'm curious of why it doesn't work now? refs are new thing for me so I think there is something wrong with them.

Here are the components:

MeasurementCameraScreen

import { useRef } from 'react'
import { StyleSheet, TouchableOpacity, View } from 'react-native'

import MeasurementCamera from '../components/MeasurementCamera'
import Text from '../components/Text'

const MeasurementCameraScreen = () => {
    const cameraRef = useRef(null)

    return (
        <View style={styles.container}>
            <View style={styles.cameraContainer}>
                <MeasurementCamera ref={cameraRef}/>
            </View>
            <View>
            </View>
            <TouchableOpacity
                onPress={() => cameraRef.current.takePicture()}
                style={styles.buttonContainer}
            >
                <Text>
                    Take image
                </Text>
            </TouchableOpacity>
        </View>
    )

}

const styles = StyleSheet.create({
    container: {
        flex: 1,
    },
    cameraContainer: {
        flex: 1,
    },
    buttonContainer: {
        width: '100%',
        height: 70,
        backgroundColor: 'white',
        justifyContent: 'center',
        alignItems: 'center',
        alignSelf: 'flex-end'
    },
})

export default MeasurementCameraScreen

MeasurementCamera

import { useState, useEffect, useImperativeHandle, forwardRef } from 'react'
import { StyleSheet } from "react-native"
import { Camera } from 'expo-camera'

import Text from './Text'

const MeasurementCamera = forwardRef((props, ref) => {
    const [hasPermission, setHasPermission] = useState(null)

    useEffect(() => {
        const getPermission = async () => {
            const { status } = await Camera.requestCameraPermissionsAsync()
            setHasPermission(status === 'granted')
        }
        getPermission()

    }, [])

    const takePicture = async () => {
        if (ref) {
            console.log(ref.current)
            const options = {
                quality: 1,
                base64: true
            }
            const picture = await ref.current.takePictureAsync(options)
            console.log(picture.uri)
        }
    }

    useImperativeHandle(ref, () => ({
        takePicture
    }))

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

    return (
       <Camera
            ref={ref}
            style={StyleSheet.absoluteFillObject}
        />
    )
})

MeasurementCamera.displayName = 'MeasurementCamera'

export default MeasurementCamera

Solution

  • Okay I found the solution!

    After reading the medium article @LouaySleman recommended about the expo-camera I understood that to be able to use the Expo Camera components functions I need to use ref. So what I needed was two refs, one for the components to communicate and another one to be able to use the Camera takePictureAsync() function.

    Now that we have permissions to access the Camera, you should get familiar with the ref props on line 132, in the Camera component. There we have passed the cameraRef that was previously defined with useRef. In doing so, we will have access to interesting methods that we can call to control the Camera.