React: InvalidStateError when trying to create a Media Element Source from an Audio element

I’m using React v17.0.1.

In one component, component A, I have an Audio element. The JSX looks like:

<audio ref={handleAudioRef} hidden={true} preload="auto" id={`audio-ref-id-${pk}`} key={pk}>
  <source src={audio} />
</audio>

where handleAudioRef looks like:

 const handleAudioRef = (r) => {
      audioRef.current = r;
  }

Note that audioRef is a context initiated as audioRef = useRef(null);

In another component, I would like to access this same audio element and attach an Analyzer Node to it.

So I’ve tried the following in component B:

    const audioCtx =  new AudioContext();
    const analyser = audioCtx.createAnalyser();
    const source = audioCtx.createMediaElementSource(audioRef.current);

Note the audioRef is the same context as in Component A, but I get the following error:

InvalidStateError: Failed to execute ‘createMediaElementSource’ on ‘AudioContext’: HTMLMediaElement already connected previously to a different MediaElementSourceNode. how to obtain media element source node

Now I understand this is a long standing issue, see here. But I’m wondering in React specifically, what the best workaround might be (if any).

My assumption is that at some point, React/Javascript creats a MediaElementSourceNode off the original Audio element and it exists somewhere in Javascript-land. Is there any way I can get access to this MediaElementSourceNode That was created? Or is there a better way entirely to go about doing this?

If possible I would like to keep component A using an Audio element and not re-write it using an AudioContext.

Answer

There was a solution posted on SO to this problem. But asked in a different context. I lost the original link to the solution. But it comes down to the createMediaElementSource needing to be declared after the audioRef event canplaythrough It doesn’t matter if the media element is rendered in a different component.

As in:

audioRef.current.addEventListener('canplaythrough', function(){ 
  const audioCtx =  new AudioContext();
  const analyser = audioCtx.createAnalyser();
  const source = audioCtx.createMediaElementSource(audioRef.current);
}