Take a picture and record video react native - reactjs

My goal is to record a video when holding down the camera button but also take a picture when tapping the camera button. Any idea what I'm doing wrong?
const [video, setVideo] = useState(null);
const [recording, setRecording] = useState(false);
const cameraRef = createRef();
const onLongPressButton = () => {
setRecording(true);
startRecord();
};
const startRecord = async () => {
setRecording(true);
console.log("RECORDING");
if (cameraRef.current) {
setRecording(true);
const recordedVideo = await cameraRef.current.recordAsync();
setVideo(recordedVideo);
}
};
const stopRecord = async () => {
await cameraRef.current.stopRecording();
console.log("STOP RECORDING");
setRecording(false);
};
const handlePhoto = async () => {
if (cameraRef.current && !recording) {
let photo = await cameraRef.current.takePictureAsync({});
console.log(photo.uri);
} else {
stopRecord();
}
};
And here is my camera button component:
<Circle
onPress={handlePhoto}
onLongPress={onLongPressButton}
onPressOut={async () => {
await cameraRef.current.stopRecording();
console.log("STOP RECORDING");
setRecording(false);
}}
delayLongPress={50}
/>

The issue appears to be not with the camera or touch handling, but with the use of createRef instead of useRef. Note that in your case, used within a function component, createRef will create a new ref on every render. Replace it with useRef so that the reference remains the same across renders:
const cameraRef = useRef();

Related

Warning: Cannot update a component from inside the function body of a different component in React Native

i have loading screen for call all the data function.i used async function for all function call.
//NOTE: this screen loads all the data and store it in store so user will have a smother experience
const LoadingScreen = (props) => {
const gotToHomeScreen = () => {
props.navigation.replace("Home", { screen: HOME_SCREEN });
};
//NOTE: loading data here for home screen journey
const getRequiredAPIDataInStore = async () => {
GetAllFieldProp();
GetAllSalaryAPIResponse();
GetSalaryAPIResponse();
let { spinnerStateForm101 } = GetForm101API();
let { spinnerStateForm106 } = GetForm106API();
GetMessagesCountAPI();
GetMessagesAPI(props);
GetAllFormAPIResponse();
GetAllSpecificSalaryAPIResponse();
let { spinnerStateMonthly } = GetMonthlyAbsenceAPI(props);
let { spinnerStateWeekly } = GetWeeklyAbsenceAPI(props);
if (
spinnerStateMonthly &&
spinnerStateWeekly &&
spinnerStateForm106 &&
spinnerStateForm101
) {
gotToHomeScreen();
}
};
getRequiredAPIDataInStore();
export default LoadingScreen;
but i am getting warning messages for this.
Warning: Cannot update a component from inside the function body of a different component.
at src/screens/loading-screen.js:19:26 in gotToHomeScreen
at src/screens/loading-screen.js:37:6 in getRequiredAPIDataInStore
How to solve this warning messsage?
Here's the approach I would take.
const Loading = () => {
const [spinnerStateMonthly, setSpinnerStatMonthly] = useState(null);
const [spinnerStateWeekly, setspinnerStateWeekly] = useState(null);
const [spinnerStateForm106, setspinnerStateForm106] = useState(null);
const [spinnerStateForm101, setSpinnerStateForm101] = useState(null);
const gotToHomeScreen = () => {
props.navigation.replace("Home", { screen: HOME_SCREEN });
};
useEffect(() => {
// async callback to get all the data and set state
(async () => {
await GetAllFieldProp();
await GetAllSalaryAPIResponse();
await GetSalaryAPIResponse();
const { spinnerStateForm101: local101 } = GetForm101API();
const { spinnerStateForm106: local106 } = GetForm106API();
setSpinnerStateForm101(local101);
setSpinnerStateForm106(local106);
await GetMessagesCountAPI();
await GetMessagesAPI(props);
await GetAllFormAPIResponse();
await GetAllSpecificSalaryAPIResponse();
const { spinnerStateMonthly: localMonthly } = GetMonthlyAbsenceAPI(props);
const { spinnerStateWeekly: localWeekly } = GetWeeklyAbsenceAPI(props);
setSpinnerStateMonthly(localMonthly);
setSpinnerStateWeekly(localWeekly);
})();
}, []);
// effect to check for what the state is and if all the states are satisfied
// then go to the home screen
useEffect(() => {
if (spinnerStateMonthly
&& spinnerStateWeekly
&& spinnerStateForm106
&& spinnerStateForm101) {
gotToHomeScreen();
}
}, [spinnerStateMonthly, spinnerStateWeekly, spinnerStateForm101,
spinnerStateForm106]);
};

How to display image in reactjs?

I have imported the import { Avatar } from '#material-ui/core'; to display the image using the avatar
const TheHeader = () => {
let _UsersId = localStorage.getItem("UsersId")
const classes = useStyles();
const [reRender, setRerender] = useState(false);
const [profilepic, setprofilepic] = useState();
const getProfile = async () => {
const response = await Profile(_UsersId);
setprofilepic(response.data)
}
useEffect(() => {
getProfile();
}, [reRender, profilepic]);
.....
<Avatar alt="" src={profilepic} className={classes.avatar}/>
the result is like this (current)
desired result
result from api console.log(response)
Currently what is being displayed is the fallback image by the Avatar component of material ui.
This is maybe because the image url you are serving it, might be being fetched AFTER your Avatar component has loaded.
The solution to this would - reload the Avatar component using useEffect with image URL set as an observable.
Sample code :
const TheHeader = () => {
let _UsersId = localStorage.getItem("UsersId")
const classes = useStyles();
const [profilepic, setprofilepic] = useState();
const getProfile = async () => {
const response = await Profile(_UsersId);
setprofilepic(response.data[0].ProfilePhoto)
}
useEffect(() => {
getProfile();
}, [profilepic]);
.....
<Avatar alt="" src={profilepic} className={classes.avatar}/>
Now, once your img url is fetched from the endpoint, your component will reload with the correct image in place
Update getProfile function to create objectUrl from binary data:
const getProfile = async () => {
const response = await Profile(_UsersId);
const buffer = new Uint8Array(response.data)
const blob = new Blob([buffer])
setprofilepic(URL.createObjectURL(blob))
}

Take photo and video with same component react native

My goal is to be able to take a photo when tapped, and a video when pressed down and held.
Currently, my code takes a photo. But when I try trigger a recording event with onLongPress, I can't seem to get it to work.
Any ideas what I'm doing wrong?
const onLongPressButton = () => {
setLongPressed(true);
};
useEffect(() => {
if (longPressed == true) {
const startRecord = async () => {
console.log("RECORDING");
if (cameraRef.current) {
setRecording(true);
const recordedVideo = await cameraRef.current.recordAsync();
setVideo(recordedVideo);
setLongPressed(true);
}
};
startRecord();
}
}, [longPressed]);
const stopRecord = async () => {
setLongPressed(false);
console.log("STOP RECORDING");
setRecording(false);
await cameraRef.current.stopRecording();
};
const handlePhoto = async () => {
console.log("Photo");
if (cameraRef.current) {
let photo = await cameraRef.current.takePictureAsync({});
console.log(photo.uri);
}
};
And here is my component:
<Circle
onPress={handlePhoto}
onLongPress={onLongPressButton}
onPressOut={stopRecord}
delayLongPress={200}
/>

React native state not updating function

React native state not updating. Location in alert returns empty.
Help me please
function Home({ route, navigation }) {
const [location, setLocation] = useState('');
const _appStart = () => {
_location();
}
const _location = () => {
setLocation("Konum Ekle");
alert(location);
}
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
_appStart();
});
try this
const _location = useCallback(()=>{
setLocation('Hi')
},console.warn(location)
)
instead of
const _location = () => {
setLocation("Konum Ekle");
alert(location);
}
Basically setState is asynchronous. So call your alert in callback of setLocation, so once setLocation is complete your alert will comes in action.

Correct handling of React Hooks for streaming video camera to HTML Video Element

I've been trying to write a React Hook for handling steaming video captured from a users camera to an HTML Video Element. I'm having trouble working out the best way to handle initialising and de-initialising the camera and HTML Video Element.
I have attempted to add a cleanup function at the end of my hook, but my attempts have ended up with the video re-initialising repeatedly or any number of other weird bugs.
Really I'm struggling to work out how and why the cleanup function is being called. It doesn't seem to relate to the component being unmounted.
Also, I'm unsure how best to destroy the video, though there are plenty of answers for that on here already, I'm not sure I need to remove it entirely. It wouldn't hurt if it hung around, there are only half a dozen pages. I suppose I just want to stop the camera streaming when a user navigates away from the page and start it up again when they return to the video page.
Camera Video Stream Hook
import { useEffect, useState } from 'react';
const initialiseCamera = async() => await
navigator
.mediaDevices
.getUserMedia({audio: false, video: true});
export const useCamera = videoRef => {
const [isCameraInitialised, setIsCameraInitialised] = useState(false);
const [video, setVideo] = useState(null);
const [error, setError] = useState('');
const [playing, setPlaying] = useState(true);
useEffect(() => {
if(video || !videoRef.current) {
return;
}
const videoElement = videoRef.current;
if(videoElement instanceof HTMLVideoElement) {
setVideo(videoRef.current);
}
}, [videoRef, video]);
useEffect(() => {
if(!video || isCameraInitialised || !playing) {
return;
}
initialiseCamera()
.then(stream => {
video.srcObject = stream;
setIsCameraInitialised(true);
})
.catch(e => {
setError(e.message);
setPlaying(false);
});
}, [video, isCameraInitialised, playing]);
useEffect(() => {
const videoElement = videoRef.current;
if(playing) {
videoElement.play();
} else {
videoElement.pause();
}
},[playing, videoRef]);
return [video, isCameraInitialised, playing, setPlaying, error];
};
Video View
import React, {createRef} from 'react';
import { useCamera } from '../hooks/use-camera';
import { Button } from '#orderandchaos/react-components';
const VideoViewDemo = () => {
const videoRef = createRef();
const [video, isCameraInitialised, running, setPlaying, error] = useCamera(videoRef);
return (
<div>
<video
ref={videoRef}
autoPlay={true}
muted={true}
controls
width={480}
height={270}
/>
<Button
onClick={() => setPlaying(!running)}
ariaLabel='Start/Stop Audio'
>{running ? 'Stop' : 'Start'}</Button>
</div>
);
};
export default VideoViewDemo;
If you add a cleanup function within any of the useEffect which has parameters specified as a dependency array, the cleanup function will be run whenever any of the parameters change.
In order for the video cleanup to only run on unmount, you would have to pass an empty dependency array. Now since the variables inside the effect will belong to the closure at the initial run, you would need to have a ref that references those values.
You can write a cleanup hook to take care of that
const useCleanup = (val) => {
const valRef = useRef(val);
useEffect(() => {
valRef.current = val;
}, [val])
useEffect(() => {
return () => {
// cleanup based on valRef.current
}
}, [])
}
import { useEffect, useState } from 'react';
const initialiseCamera = async() => await
navigator
.mediaDevices
.getUserMedia({audio: false, video: true});
export const useCamera = videoRef => {
const [isCameraInitialised, setIsCameraInitialised] = useState(false);
const [video, setVideo] = useState(null);
const [error, setError] = useState('');
const [playing, setPlaying] = useState(true);
useEffect(() => {
if(video || !videoRef.current) {
return;
}
const videoElement = videoRef.current;
if(videoElement instanceof HTMLVideoElement) {
setVideo(videoRef.current);
}
}, [videoRef, video]);
useCleanup(video)
useEffect(() => {
if(!video || isCameraInitialised || !playing) {
return;
}
initialiseCamera()
.then(stream => {
video.srcObject = stream;
setIsCameraInitialised(true);
})
.catch(e => {
setError(e.message);
setPlaying(false);
});
}, [video, isCameraInitialised, playing]);
useEffect(() => {
const videoElement = videoRef.current;
if(playing) {
videoElement.play();
} else {
videoElement.pause();
}
},[playing, videoRef]);
return [video, isCameraInitialised, playing, setPlaying, error];
};

Resources