Reputation: 21
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
Reputation: 441
You look to be confused about some of React principles, and that is precisely what's causing your issue to happen:
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=
.
<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:
timeupdate
event triggers in a function called handleTimeUpdate
const handleTimeUpdate = () => {
if (player.currentTime - player._startTime >= player.value) {
player.pause();
}
};
<audio
id="myAudio"
onTimeUpdate={updatePlayerAudio}
preload="auto"
src="http://webstuffy.com/luis20.mp3"
type="audio/mpeg"
/>
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
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
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
https://reactjs.org/docs/hooks-effect.html
https://reactjs.org/docs/state-and-lifecycle.html
https://reactjs.org/docs/hooks-reference.html#useref
Upvotes: 2
Reputation: 71
You can use useRef like this link.
https://bobbyhadz.com/blog/react-get-element-by-id
Upvotes: 0