Reputation: 431
I'm developing a React application to record both audio and video from the user and then fetch it to a backend.
In the MediaRecorderComponent
below, the function initializeDevice
asks the user to allow audio or video recording (it's called from clicking a button since our users need our application UI to be as clear and explicit as possible) and, if granted, initializes the mediaRecorder
variable to a new MediaRecorder
object, receiving as argument the response from the navigator.mediaDevices.getUserMedia
promise.
import React, { useEffect, useState } from 'react';
import { RecordAudioButton } from '../Components';
type Props = {
mediaConstraints: MediaStreamConstraints;
};
function MediaRecorderComponent({ mediaConstraints }: Props) {
const [permissionGranted, setPermissionGranted] = useState(false);
const [stream, setStream] = useState<null | MediaStream>(null);
const [isRecording, setIsRecording] = useState(false);
let mediaRecorder: MediaRecorder;
const initializeDevice = async () => {
try {
const response = await navigator.mediaDevices.getUserMedia(
mediaConstraints
);
setStream(response);
setPermissionGranted(true);
mediaRecorder = new MediaRecorder(response);
} catch (err) {
setPermissionGranted(false);
console.log(err);
}
};
const handleRecording = () => {
if (isRecording) {
mediaRecorder.stop();
setIsRecording(false);
}
mediaRecorder.start();
setIsRecording(true);
};
return (
<div>
{permissionGranted ? (
<RecordAudioButton
isRecording={isRecording}
onClick={handleRecording}
/>
) : (
<>
<p>
CLIENT INSTRUCTIONS
</p>
<button
type="button"
onClick={() => initializeDevice()}
>
Let's start!
</button>
</>
)}
</div>
);
}
Later on, the handleRecording
is passed as a prop to another component, which calls it when its div
is clicked on:
function RecordAudioButton({ isRecording, onClick }: Props) {
return (
<div
className="bg-error text-base-100 rounded-full p-5 shadow-lg"
onClick={() => onClick()}
>
///... and so on
However, when the button is clicked I receive a Uncaught TypeError: Cannot read properties of undefined (reading 'start')
triggered when the handleRecording
function calls mediaRecorder.start()
, and the mediaRecorder
variable appears as undefined
, even although it was initialized when the user granted permission in the initializeDevice
function.
Why would the reference be uninitialized?
Upvotes: 0
Views: 1343
Reputation: 17387
Use a ref to store your value:
import React, { useEffect, useState, useRef } from 'react';
...
const mediaRecorder = useRef(null);
const initializeDevice = async () => {
try {
const response = await navigator.mediaDevices.getUserMedia(
mediaConstraints
);
setStream(response);
setPermissionGranted(true);
mediaRecorder.current = new MediaRecorder(response);
} catch (err) {
setPermissionGranted(false);
console.log(err);
}
};
const handleRecording = () => {
if (isRecording) {
mediaRecorder.current.stop();
setIsRecording(false);
}
mediaRecorder.current.start();
setIsRecording(true);
};
...
Changing the values in your ref will not cause a rerender and will persist the value even when a render occurs provided that the component is not unmounted.
useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.
Upvotes: 1