creas443
creas443

Reputation: 21

HTML5 audio component from within React

I coded some javascript to play an interval within an mp3 file, which works fine in plain html/javascript

<audio id="myAudio" preload="auto" src="http://filename.mp3" type="audio/mpeg" />

https://jsfiddle.net/vz03m41p/4/

But when I copy it into React, I get a javascript error.

<audio ref="myAudio" preload="auto" src="http://filename" type="audio/mpeg" />

https://codesandbox.io/s/infallible-turing-d29sp5?file=/src/App.js

Anybody have any idea what I'm doing wrong in the React version?

Upvotes: 0

Views: 3142

Answers (3)

hhhhhhh
hhhhhhh

Reputation: 441

You look to be confused about some of React principles, and that is precisely what's causing your issue to happen:

  • If you are using a ref= attribute, you'll need to assign it to a variable, that will be assigned to an empty useRef(), which will contain that element.

For instance, here's what it would looks like:

const myAudio = useRef();
<audio ref={myAudio} />

Then only, your audio be linked to that reference you made using ref=.

  • Another point, React has included its own event listeners that you can directly use into your <input>, <audio> and so on HTML elements. Here, you can link a function to be executed each time your timeupdate event get triggered like that:

<audio ref={myAudio} onTimeUpdate={handleTimeUpdate} />

At each time onTimeUpdate (the react equivalent of that listener) is triggered, handleTimeUpdate will be run over. We now know enough to achieve your goal, let's build it together:

  • Let's isolate what happens when the timeupdate event triggers in a function called handleTimeUpdate
const handleTimeUpdate = () => {
    if (player.currentTime - player._startTime >= player.value) {
      player.pause();
    }
  };
  • Great job, now let's add up all we've learned about HTML elements together into the tag:
  <audio
    id="myAudio"
    onTimeUpdate={updatePlayerAudio}
    preload="auto"
    src="http://webstuffy.com/luis20.mp3"
    type="audio/mpeg"
  />

  • Awesome, now let's move all of this under the function App() component.

Done, all of it now works under React logic. You can check the code here, and ask for any more questions below, I'll make sure to edit.

The good thing about this method is that you'll be safe of any recurring addEventListener issue, and of any misfunction of your thing, as it'll only runs ever if that is triggered.

Upvotes: 1

Sebastian Gudi&#241;o
Sebastian Gudi&#241;o

Reputation: 363

When you are working with react components you have to keep in mind a concept called the life cycle, The fact that you have already declared your component does not mean you can now just call document.getElementById on it, since at that point, the component has not been rendered initially yet.

Then how could you add an event listener on an HTML tag inside a react component? You use a ref and ditch the "Element by id" approach. A ref is basically a variable that react uses to give you access to the DOM attributes of the tags without having to deal with selectors or the component life cycle and they look kinda like this

function App({ children }) {
    //Here we create the ref
    const audioRef = useRef(null);
    return (
        <div>
            click1
<audio ref={audioRef} preload="auto" src="http://webstuffy.com/luis20.mp3" type="audio/mpeg" />

    <button onClick={()=>{play_audio('myAudio',7,2)}}>2 sec</button> 
      </div>
    );
  }

Now that audioRef variable could be used to add your desired event listener like this (Keep reading tho, this one is not the final answer)

function App({ children }) {
    //Here we create the ref
    const audioRef = useRef(null);
    
    //Here we add the event
    //BUT this will run multiple times...
    audioRef.current.addEventListener("timeupdate", function() {
     if (player.currentTime - player._startTime >= player.value){    
        player.pause(); 
      };
    })

    return (
        <div>
            click1
<audio ref={audioRef} preload="auto" src="http://webstuffy.com/luis20.mp3" type="audio/mpeg" />

    <button onClick={()=>{play_audio('myAudio',7,2)}}>2 sec</button> 
      </div>
    );
  }

And that would work in theory, but again, keep the lifecycle in mind, if we do things this way the addEventListener line will happen every time the component re-renders and we will end up with a LOT of event listener over time, we need a way to make this line of code run only when the componen is initially created. Well, another react hook comes to the rescue here. useEffect()! This hook is kinda complex at the beginning, but for our purposes right now, if we pass to it a function and an empty array, it will run said function only once when the component is first created. Which is just what we need, the final code would look something like this:

function App({ children }) {
    //Here we create the ref
    const audioRef = useRef(null);
    
    //This will run only once
    useEffect(() => {

      //Here we add the event
      audioRef.current.addEventListener("timeupdate", function() {
        if (player.currentTime - player._startTime >= player.value) {    
          player.pause(); 
        };
      })

    },[])

    return (
        <div>
            click1
<audio ref={audioRef} preload="auto" src="http://webstuffy.com/luis20.mp3" type="audio/mpeg" />

    <button onClick={()=>{play_audio('myAudio',7,2)}}>2 sec</button> 
      </div>
    );
  }

And of course, here is your, now error free, example

https://codesandbox.io/s/compassionate-dubinsky-tcpqtz?file=/src/App.js

Edit compassionate-dubinsky-tcpqtz

If you are not very clear on WHY we have to do things this way and we cannot just access the DOM like we are used to in vanilla JS, then you might need to read a little bit more on what components actually are and how the component life cycle looks like, this links in the official react documentation might provide additional clarification

Info about use effect

https://reactjs.org/docs/hooks-effect.html

Info about components and their lifecycles

https://reactjs.org/docs/state-and-lifecycle.html

Info about useRef and references

https://reactjs.org/docs/hooks-reference.html#useref

Upvotes: 2

A.Aydın
A.Aydın

Reputation: 71

You can use useRef like this link.

https://bobbyhadz.com/blog/react-get-element-by-id

Upvotes: 0

Related Questions