Home > OS >  Reference to MediaRecorder object is undefined in function, despite being initialized in previously
Reference to MediaRecorder object is undefined in function, despite being initialized in previously

Time:11-02

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?

CodePudding user response:

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.

  • Related