skip to Main Content

I’m trying to figure out a way of controlling HTML Audio elements that are in components in React.

I’m making a music viewer which has buttons that allow you to pause or play all individual audio elements at once.

I’ve tried using props to pass the state about whether or not all the samples should play, but this causes the page to refresh in a way that resets the audio elements to their default position(not playing).

Any suggestions or pointers as to how I could approach this would be much appreciated, thanks.

Image of the Track Editor

Home Page:

'use client';

import Track from './components/track.tsx';

import {useState, useRef} from 'react';


export default function Home() {






  var musicDir = "/assets/music"
  var currentSong = "sweep-me-off-my-feet";



  const [playingStatus, updatePlaying] = useState(null);



  const musicFolder = "./assets/music"
  const currentSongFolder = "/sweep-me-off-my-feet/"

  return (
    <div id="viewer-widget" className="flex-row h-min w-min bg-gray-500 m-0 mt-20 ml-5 pt-1 pb-6 px-10 rounded-lg">
      <div id="track-container">

      </div>

      <h1 className="text-3xl text-center text-white my-5 underline">Music Viewer</h1>

      <button onClick={() => playingStatus.current = true}>Play All</button>

      <button onClick={() => playingStatus.current = false}>Stop All</button>

      <Track source={musicFolder + currentSongFolder + "pond-sweepme-lead.mp3"} isPlaying={playingStatus.current} />
      <Track source={musicFolder + currentSongFolder + "pond-sweepme-strings.mp3"} isPlaying={playingStatus.current} />
      <Track source={musicFolder + currentSongFolder + "pond-sweepme-bass.mp3"} isPlaying={playingStatus.current} />
      <Track source={musicFolder + currentSongFolder + "pond-sweepme-brass.mp3"} isPlaying={playingStatus.current} />


  
    </div>


  )
}



All</button>

Audio Component File:

const Track = ({source, isPlaying}) => {


    const myaudio = useRef(null); //useRef(null);


    const [val, setVal] = useState(84);
    const inputRef = useRef(null);




    const MyAudio = React.forwardRef((props, ref) => {

        return (

                    
        <audio controls controlsList="nodownload noplaybackrate" ref={ref} src={source} type="audio/mp3" className="">
            Your browser does not support the audio element.
            {props.children}
        </audio>

        );

    });

    const [audio1, setAudio1] = useState(null)
    
    useEffect(() => {

      setAudio1(new Audio(source));
    }, [])


    ReactDOM.render(audio1, document.getElementById("track-container"));




    function updateVolume(event) {

        myaudio.current.volume = event.target.value / 100;

    }

    useEffect(() => {
        inputRef.current.addEventListener("input", updateVolume);
    }, []);



    if (isPlaying == false) {






        console.log("current time through track: " + myaudio.current.currentTime)


        myaudio.current.pause();



    } else if (isPlaying == true) {




        myaudio.current.play();

    }

    

    
    return (


        <div className="track flex">

            <MyAudio ref={myaudio}/>

            <div className="flex audio-controls w-32 h-15 bg-white rounded-lg ml-2">
                <input onChange={updateVolume} ref={inputRef} type="range" id="volume-knob" min="0" max="100" value={val} data-diameter='50' data-bgcolor="#a8a7b5" data-fgcolor="#4a4a4a" className="input-knob volume-knob outline-none focus:outline-none outline-transparent border-transparent focus:ring-0"/>
                
            </div>

        </div>

    );
};

export default Track;

2

Answers


  1. A simple example doing what I understand you want: https://codepen.io/aSH-uncover/pen/ZEVaOBa

    IMHO, You dont really need to use the <audio> markup and you can stick with the Audio object.
    This is what the ‘Track’ component looks like on this codepen:

    const Track = ({ src, playing }) => {
      
      // Hooks //
      
      const audio = useMemo(() => {
        return new Audio(src)
      }, [src])
      
      useEffect(() => {
        if (playing) {
          audio.play()
        } else {
          audio.pause()
        }
        return () => audio.pause()
      }, [playing])
      
      // Rendering //
      
      return (
        <li>
          {src}
        </li>
      )
    }
    
    Login or Signup to reply.
  2. You are recreating the MyAudio component on each render. When re-create the component itself, the previous component is unmounted, and the new one us mounted. Move it’s definition out of Track:

    const MyAudio = React.forwardRef((props, ref) => {
    ...
    });
    
    const Track = ({ src, playing }) => {
    ...
    });
    

    If you want to render a component into another DOM element, use createPortal. However, it’s not clear why you’re trying to render audio1, which is new Audio() into the DOM, and why you do it on each render:

    ReactDOM.render(audio1, document.getElementById("track-container"));
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search