I am using React Functional Components in my app created with create-react-app, so React.StrictMode is automatically enabled. I don't want to disable it as it is helping me spot potential issues as am developing. Trouble is that it makes the component render twice which creates an error:
Uncaught DOMException: Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode as the HTMLMediaElement gets connected in the first render. I am wondering, is there a way to disconnect the HTMLMediaElement from the previous MediaElementSourceNode... maybe set it to null
or something, so that it can get a fresh connection?
Here below, is my code that throws the error:
import React, { useEffect, useRef } from "react";
const Canvas = () => {
const audioRef = useRef(null);
const filUploadRef = useRef(null);
const canvasRef = useRef(null);
let audioSource;
let analyser;
let dataArray;
useEffect(() => {
const audioElement = audioRef.current;
const canvas = canvasRef.current;
const fileUploadField = filUploadRef.current;
fileUploadField.addEventListener("change", () => {
const audioContext = new AudioContext();
const files = fileUploadField.files;
audioElement.src = URL.createObjectURL(files[0]);
audioElement.load();
audioElement.play();
audioSource = audioContext.createMediaElementSource(audioElement);
analyser = audioContext.createAnalyser();
audioSource.connect(analyser);
analyser.connect(audioContext.destination);
dataArray = new Uint8Array(analyser.frequencyBinCount);
});
}, []);
const handleTogglePlayPause = () => {
//TODO: Implement toggle Play/Pause when the button is clicked.
};
return (
<>
<canvas ref={canvasRef} />
<audio controls ref={audioRef} />
<input type="file" id="file-upload" accept="audio/*" ref={filUploadRef} />
<button onClick={handleTogglePlayPause} type="button">
{/* ^ This button element is related to handleTogglePlayPause function for a different stage*/}
Play/Pause
</button>
</>
);
};
export default Canvas;
You can check if audioSource
already has a source, and if so you can try to avoid creating a new media element source. Also, you do not need to create a new analyser and connect it.
Add this if check to your code, and it should solve the problem.
// ..
audioElement.play();
if (!audioSource) {
audioSource = audioContext.createMediaElementSource(audioElement);
analyser = audioContext.createAnalyser();
audioSource.connect(analyser);
analyser.connect(audioContext.destination);
}
//..
Well, react strict mode helps identifying problems, but at the same time it creates new ones. Hence, you might consider closing the strict mode at all.