最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Expo-Camera, takePictureAsync undefined (Unhandled promise rejection: TypeError: ref.current.takePictureAsync is no

programmeradmin2浏览0评论

I'm trying to create react app with expo using expo-camera for taking pictures. I have separately ponents 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 bine the MeasurementCameraScreen and MeasurementCamera ponents to one ponent, 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 ponents:

MeasurementCameraScreen

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

import MeasurementCamera from '../ponents/MeasurementCamera'
import Text from '../ponents/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

I'm trying to create react app with expo using expo-camera for taking pictures. I have separately ponents 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 bine the MeasurementCameraScreen and MeasurementCamera ponents to one ponent, 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 ponents:

MeasurementCameraScreen

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

import MeasurementCamera from '../ponents/MeasurementCamera'
import Text from '../ponents/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
Share Improve this question edited Feb 27, 2023 at 12:35 vvvvv 32.2k19 gold badges65 silver badges100 bronze badges asked Aug 24, 2022 at 8:27 katjasdfkatjasdf 111 silver badge4 bronze badges 5
  • The error basically means that ref.current is undefined. Don't worry about takePictureAsync for now, you need to fix the ref part. console.log() stuff to debug this. – user5734311 Commented Aug 24, 2022 at 8:29
  • @ChrisG with console.log(ref) I get Object { "current": Object { "takePicture": [Function _callee2], }, } before the error – katjasdf Commented Aug 24, 2022 at 8:35
  • That looks like the camera object has a takePicture function but no takePictureAsync function – user5734311 Commented Aug 24, 2022 at 8:40
  • @ChrisG Tried it and got RangeError: Maximum call stack size exceeded. error. In expo documentation there is only takePictureAsync function, no takePicture function docs.expo.dev/versions/latest/sdk/camera – katjasdf Commented Aug 24, 2022 at 9:00
  • I'll add an example and please tell me if it's work for you – Louay Sleman Commented Aug 24, 2022 at 9:42
Add a ment  | 

2 Answers 2

Reset to default 4

What you needed is to add two refs, one for the ponents to municate and another one to be able to use the Camera takePictureAsync() function Please check this example from medium:

import React, { useState, useRef, useEffect } from "react";
import {
 View,
 Text,
 TouchableOpacity,
 SafeAreaView,
 StyleSheet,
 Dimensions,
} from "react-native";
import { Camera } from "expo-camera";
import { Video } from "expo-av";
export default function CameraScreen() {
 const [hasPermission, setHasPermission] = useState(null);
 const [cameraType, setCameraType] = useState(Camera.Constants.Type.back);
 const [isPreview, setIsPreview] = useState(false);
 const [isCameraReady, setIsCameraReady] = useState(false);
 const [isVideoRecording, setIsVideoRecording] = useState(false);
 const [videoSource, setVideoSource] = useState(null);
 const cameraRef = useRef();
 useEffect(() => {
    (async () => {
 const { status } = await Camera.requestPermissionsAsync();
 setHasPermission(status === "granted");
    })();
  }, []);
 const onCameraReady = () => {
 setIsCameraReady(true);
  };
 const takePicture = async () => {
 if (cameraRef.current) {
 const options = { quality: 0.5, base64: true, skipProcessing: true };
 const data = await cameraRef.current.takePictureAsync(options);
 const source = data.uri;
 if (source) {
 await cameraRef.current.pausePreview();
 setIsPreview(true);
 console.log("picture", source);
      }
    }
  };
 const recordVideo = async () => {
 if (cameraRef.current) {
 try {
 const videoRecordPromise = cameraRef.current.recordAsync();
 if (videoRecordPromise) {
 setIsVideoRecording(true);
 const data = await videoRecordPromise;
 const source = data.uri;
 if (source) {
 setIsPreview(true);
 console.log("video source", source);
 setVideoSource(source);
          }
        }
      } catch (error) {
 console.warn(error);
      }
    }
  };
 const stopVideoRecording = () => {
 if (cameraRef.current) {
 setIsPreview(false);
 setIsVideoRecording(false);
 cameraRef.current.stopRecording();
    }
  };
 const switchCamera = () => {
 if (isPreview) {
 return;
    }
 setCameraType((prevCameraType) =>
 prevCameraType === Camera.Constants.Type.back
        ? Camera.Constants.Type.front
        : Camera.Constants.Type.back
    );
  };
 const cancelPreview = async () => {
 await cameraRef.current.resumePreview();
 setIsPreview(false);
 setVideoSource(null);
  };
 const renderCancelPreviewButton = () => (
 <TouchableOpacity onPress={cancelPreview} style={styles.closeButton}>
 <View style={[styles.closeCross, { transform: [{ rotate: "45deg" }] }]} />
 <View
 style={[styles.closeCross, { transform: [{ rotate: "-45deg" }] }]}
 />
 </TouchableOpacity>
  );
 const renderVideoPlayer = () => (
 <Video
 source={{ uri: videoSource }}
 shouldPlay={true}
 style={styles.media}
 />
  );
 const renderVideoRecordIndicator = () => (
 <View style={styles.recordIndicatorContainer}>
 <View style={styles.recordDot} />
 <Text style={styles.recordTitle}>{"Recording..."}</Text>
 </View>
  );
 const renderCaptureControl = () => (
 <View style={styles.control}>
 <TouchableOpacity disabled={!isCameraReady} onPress={switchCamera}>
 <Text style={styles.text}>{"Flip"}</Text>
 </TouchableOpacity>
 <TouchableOpacity
 activeOpacity={0.7}
 disabled={!isCameraReady}
 onLongPress={recordVideo}
 onPressOut={stopVideoRecording}
 onPress={takePicture}
 style={styles.capture}
 />
 </View>
  );
 if (hasPermission === null) {
 return <View />;
  }
 if (hasPermission === false) {
 return <Text style={styles.text}>No access to camera</Text>;
  }
 return (
 <SafeAreaView style={styles.container}>
 <Camera
 ref={cameraRef}
 style={styles.container}
 type={cameraType}
 flashMode={Camera.Constants.FlashMode.on}
 onCameraReady={onCameraReady}
 onMountError={(error) => {
 console.log("camera error", error);
        }}
 />
 <View style={styles.container}>
 {isVideoRecording && renderVideoRecordIndicator()}
 {videoSource && renderVideoPlayer()}
 {isPreview && renderCancelPreviewButton()}
 {!videoSource && !isPreview && renderCaptureControl()}
 </View>
 </SafeAreaView>
  );
}
const WINDOW_HEIGHT = Dimensions.get("window").height;
const closeButtonSize = Math.floor(WINDOW_HEIGHT * 0.032);
const captureSize = Math.floor(WINDOW_HEIGHT * 0.09);
const styles = StyleSheet.create({
 container: {
    ...StyleSheet.absoluteFillObject,
  },
 closeButton: {
 position: "absolute",
 top: 35,
 left: 15,
 height: closeButtonSize,
 width: closeButtonSize,
 borderRadius: Math.floor(closeButtonSize / 2),
 justifyContent: "center",
 alignItems: "center",
 backgroundColor: "#c4c5c4",
 opacity: 0.7,
 zIndex: 2,
  },
 media: {
    ...StyleSheet.absoluteFillObject,
  },
 closeCross: {
 width: "68%",
 height: 1,
 backgroundColor: "black",
  },
 control: {
 position: "absolute",
 flexDirection: "row",
 bottom: 38,
 width: "100%",
 alignItems: "center",
 justifyContent: "center",
  },
 capture: {
 backgroundColor: "#f5f6f5",
 borderRadius: 5,
 height: captureSize,
 width: captureSize,
 borderRadius: Math.floor(captureSize / 2),
 marginHorizontal: 31,
  },
 recordIndicatorContainer: {
 flexDirection: "row",
 position: "absolute",
 top: 25,
 alignSelf: "center",
 justifyContent: "center",
 alignItems: "center",
 backgroundColor: "transparent",
 opacity: 0.7,
  },
 recordTitle: {
 fontSize: 14,
 color: "#ffffff",
 textAlign: "center",
  },
 recordDot: {
 borderRadius: 3,
 height: 6,
 width: 6,
 backgroundColor: "#ff0000",
 marginHorizontal: 5,
  },
 text: {
 color: "#fff",
  },
});

Okay I found the solution!

After reading the medium article @LouaySleman remended about the expo-camera I understood that to be able to use the Expo Camera ponents functions I need to use ref. So what I needed was two refs, one for the ponents to municate 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 ponent. 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.

发布评论

评论列表(0)

  1. 暂无评论