Build a Fullstack Music App with Next.js Seek Bar Controls
A second version of this course released using Next 13+, but this course is great if you want to see another example fullstack project. We now recommend you take the Build a Fullstack App with Next.js, v2 course.
Transcript from the "Seek Bar Controls" Lesson
>> Yeah, let's keep moving, so now we'll work on the onEnd. So the onEnd is gonna be a handler for when a song ends, what do you wanna do? We'll typically in a music player if a song ends, I wanted to go to the next song automatically. So we have to handle that so I'm gonna say OnEnd and that's gonna be a function and what I wanna do here is basically we have to check for repeat in here.
[00:00:25] So typically, in the music player, if you hit Next, it does account for shuffle, but it doesn't usually account for repeat. If I hit Next when repeat is on, does it just play the same song, or does it go to the next song and then repeat that song?
[00:00:40] I think they'd goes to the next song it repeats the next song, maybe you would, if you hit next but repeat is on I think you'd expected it to go to the next song why else would you hit next? So that's why we don't account for repeat on next but we will account for repeat on song end.
[00:00:54] So therefore it just loops back and plays that song again, so we'll do that and what we'll say is, we'll say if and we're keeping track of the repeat state so this should work so we'll say if repeat, is activated. What we'll do is we basically have to set the seek manually, so we have to tell react holler to seek back to zero so reset the song basically.
[00:01:22] And we can do that using our sound ref, so we can say sound ref being at the current which means give me the current value of this reference. This will be the react instance or direct Howler instance and that has a method on it called seek and we can give it a number of zero so just saying seek back to zero And that's all if the repeat is activated, if repeat is not activated, then just go to next song, so call the next song function.
[00:01:55] We're also keeping track of seek ourself in our state, and we're gonna use that to move the animation. So this is setting the react hours, so the actual song to restart, but to update the UI to restart we also have to keep track of our seek and let's say setSeek to zero as well.
[00:02:14] So we're gonna use that seat state to keep track of the UI, but the second one is to actually move the song back to zero, so two different things. Okay, the other one we wanna take care of is the onLoad event, so when a song loads up. We need to grab the duration from it and set the duration, so we can actually show it in the player at the bottom right.
[00:02:45] So we'll say const onLoad that's gonna equals something here and we're just gonna say const song, duration that's gonna equals the soundRef.current.seek. However, has a method on it called duration, if you call it, it'll give you back the current duration of the song that just loaded up. So we'll get that duration back, and then we'll just say set duration to whatever the song duration was.
[00:03:18] That way we can update the duration on the bottom right of the Seek bar, again, all the songs have durations now saved in a database, but they're like, in a different format. So this is, I'm just grabbing the state from whatever react holler says, that way, it's guaranteed to be the same, I don't want any like millisecond differences or second differences.
[00:03:39] So I'm just like whatever that thing says the duration is, that's what it is and hopefully the database matches with that, so. Okay, any questions so far on some of these callbacks that we did? We just did a bunch of callbacks here. Okay so now we want to hook these up into the DOM or actually there's one more we wanna do.
[00:04:06] We wanna handle what happens when you do the seeking, so if you click the seek bar, if you drag the seek bar, we wanna do some stuff there. So we'll say const onSeek we wanna do that that's going to take an event and we wanna just set the seek value to, We got a parse float because we're doing decimals on the seek value because they step by point one every time.
[00:04:30] So we got to parse float, not parse int and the range components spits out an array of values, because we had like a minimum and maximum. So it'll spit back an array of values for some reason and not just one value, I know. So we have to do is just get E and get the first value out of there.
[00:04:52] That's only unique to this range component from chakra if you use like the range component from html5, it just spits out one value, it doesn't spit out an array of values. But this one does have something to do with the min and the max and the fact that I get multiple thumbs and things like that.
[00:05:07] So we got that, so that's gonna update the visual UI seeking, but then now we have to also update the song to seek to it as well. And the way we do that is using the sound ref dot current, it also has a seek method that we seen before.
[00:05:25] And you can pass whatever you want, we set it to 0 up here to reset the song, he's got to pass it to whatever this value is to seek to that value. Cool, okay, so now let's attach these handlers to all the elements that need to they need them.
[00:05:47] So for react taller we're going to add the onLoad and the onEnd, so we'll say onLoad we're going to say they on load like that and then onEnd we're gonna do that on and like that And then for the other ones that we have, what do we have, we had onSeek, we had next song and previous song.
[00:06:18] And I think those are the only two that we need so, the next song and previous song are exactly where you think they would go so previous song is gonna go here on click, we're gonna say previous song. Then the next song is gonna go exactly where you think it's gonna go on the next button.
[00:06:40] So we'll say on click next song. And then the last one is gonna be for the seeking so we got to handle the onSeek. The way we're going to do that is if we go down to the rain slider, the main one here, we're going to do a couple things.
[00:07:00] First, we need to give it a max value, so it knows how big it should be, so we're gonna say max. And then here's where we're gonna use the duration that we've been setting. So we're gonna say, hey, if there is a duration, then we're gonna say, duration.toFixed.
[00:07:15] And we're gonna say, 2. This just is gonna just fix the decimal places, so we get the right amount of decimal places for this number. So it looks like the way it's supposed to look, if not then just zero, we don't have anything yet. And this thing is freaking out because no duplicate prompts, I already have one there and let me get rid of, am I there we go?
[00:07:40] Why else are you freaking out? Okay, I don't know what that type varies but it's totally fine. And then we need to do an onChange. So onChange we'll do the onSeek, so we'll say onSeek like that, that'll be for the onChange And this will handle when someone clicks the sick bar and it tracks.
[00:08:16] And then what we wanna do now is we wanna handle when someone is dragging it, so we got to keep track of if someone is currently seeking or not. And those gonna be two call backs for that before we get there we'll add the value of whatever it currently is, and that's just gonna be an array with seek.
[00:08:35] The reason I'm putting an array is because that's literally what range slider wants. It wants an array because this range slider can accept multiple values, and I guess they just didn't feel like figuring out if you pass in a single value or not. Which is easy for them to do, so they just like you always got to pass an array no matter what, so we have to pass an array with the current seek value.
[00:08:57] And the next thing we got to do is, again, we need to listen for when someone is currently seeking as they're dragging the thumb left and right. Because we're gonna do certain things when that's true, so there's two events for that, we'll say onChange start, Like this, and I'm gonna copy that and there's another onChange end.
[00:09:20] So we basically like I said, we're just going to keep track of whether you are seeking or not, we don't have any state for that right now. So we got to come back up here and make a new states and that's just going to be called, is seeking will say is seeking and set is seeking And they'll start to false.
[00:09:59] Okay, so now that we have that, we can go back down, and on chain start, all we have to do is just say set is seeking is true. Cuz you're starting to do the change onChange and we can say set seeking is false, you are done seeking. Okay, we got all of that hooked up, there's still some more work to do with, some things we need to handle, but so far everything seems to be working.
[00:10:28] The last thing we wanna do, is let's see, we wanna make sure that we're playing the right song, actually I think we're good there, so.
>> Can we control the volume levels also with this?
>> Yeah, you can a hundred percent control the volume levels of, with holler, there's a prompt that you can pass in that allows you to control the volume.
[00:10:46] So for the UI that we have, there is a volume on the bottom right, about a volume slider, you can literally control that with react collar, so that's how it works.
>> With this player be a good candidate for X state?
>> Put this player be a good candidate for X state, so if you don't know what x state is, X state is a finite state machine that turns your UI logic into a state tree kind of like a router.
[00:11:17] Yeah, I think it would, I mean, you would have to build a really nice interpreter, well I think X date has a really good interpreter now. Because you have to tie in react Aller to it some lot somehow, but once you figure that out you had a really good interpreter yeah I think X date will be a great one.
[00:11:34] Because you can only be in a certain state at a time, so I mean, I think x state is great for all UI. So yeah, totally, this is a great candidate, I think a lot of people don't like using X state though, because of all the setup that you have to do, but I'm a big fan of it.
[00:11:47] I rather do the setup on X state and have like a predictable guaranteed UI that's like bug free versus not setting it up. And just being like going through all these conditionals and if statements inside of JSX yeah rather not. So, yeah, I think it's a great candidate for state.
[00:12:03] So let's see where we are now, we're gonna try to click a button. I click the button, I can see that the seek, like resettled, like some something happened here, it's not quite there yet the duration thing still isn't there. So we need to go back, we need to make sure that the seek is being uploaded every single tick, so for nice, smooth animation, so we got to figure that out.
[00:12:26] We need to go back and fix this duration that's not showing the right thing. Once we fix that, we can test whether or not the repeat is working and things like that onEnd. Because right now like, you can see the seek is like getting there but it's not moving, so we don't actually know where the song is, but.
[00:12:45] If you listen right quick, I'm gonna just play my a little bit, when I seek, you can hear it actually seeking, so it does seek from the songs perspective, but the UI is not being updated. So we need to update the UI so that that bar actually moves in and then we need to fix this description or here, this hard coded thing.
[00:13:06] Okay, and let's do that. So to fix that duration, let's go down to where that actually is, I believe it's at the bottom here, I just have a hard coded number. Instead, what we're gonna do is we're gonna use one of our formatters, the format time helper that we have we're gonna pull that in.
[00:13:23] So we're gonna say formatTime from lib/formatters, that's just gonna take in a time here, which is just got to be duration. So once we pass that in, we should have the proper duration for a song that gets loaded up. So if we go check that you can see right now I do have the proper duration for this song, it's 4 minutes and 14 seconds.
[00:13:44] Apparently even though the database says three minutes and 58 seconds so this is why I'm trusting react taller and not all the fake data that I put in a database.