Build a Fullstack Music App with Next.js End Song & Repeat Handlers
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 "End Song & Repeat Handlers" Lesson
>> So now let's finish off this playing, and we can get this thing deployed. So let's take note of the current issues that we have. So let's go through this app, and we can see what that is and then we'll fix those issues. So, if I hit play, I'm gonna turn this down, and then I seek to the end, or actually, let's just hit next and see what happens.
[00:00:19] First of all, that doesn't do anything so we got it we're not there yet, next is literally not doing anything. And previous is literally not doing anything, so although we do have handlers for that those handlers are just setting an index. We're not doing anything with that index, so we need to do something with that.
[00:00:37] If we go to the end of the track and just let it in naturally by itself, it should go back to the next it should go to the next songs I don't have repeat set, but as you can see, it didn't it just stayed on the current song.
[00:00:52] So we got to fix that, the other thing we need to fix is, is if I have repeat on well, you're not gonna be able to observe this book because it is actually going to repeat because it's not going next. But I promise you that doesn't work either and the same thing with shuffle if I hit shuffle, and then I hit next which doesn't work that's not gonna work either.
[00:01:12] So we got to fix all of those things. So let's do it let's head back to the code the first thing is we need to Iddress one issue is that when you click on one of these songs to load it up. Right now we are setting the whereas as we're setting the index that's being played to zero.
[00:01:36] So to always be the first thing we need to set it to whatever the index of the active song that that got passed in. We need to set it to that, so Instead of putting zero on this use state, we're instead going to say the initial state is gonna be songs dot find index of whatever some song ID that triple equals some active song.
[00:01:58] ID, so, whatever that index is start there so that's the first change. Making sure we set the initial index to be whatever the index of the active song is and the current playlist so it doesn't reset to zero no matter what song we click on. And then the next thing is we need to actually use this index to play the current song.
[00:02:19] So in order to do that, we know that the player is bound to whatever the act of song is. This so how do we actually take advantage of using the index well if we use the index to set the active song that would trigger a state change through easy peasy.
[00:02:35] Which will then rerender the component with the correct active song so that's all we need to do is to use R actions from easy peasy to set an active song based off the index. And now we got to think about, well, when does the song change. The song changes on end, it changes on previous, changes on next.
[00:02:55] So we have to put in all three of those places or we can just make a use effect that watches when the index changes. And when the index changes, then we can set it active song, so let's do that. So let's make a new use effects I don't need this one, this is fine and we'll say use effec, and what this one's gonna do is it's going to watch the current index it's also going to need reference to an action.
[00:03:22] Called, like the action that we have from our stored actions which we need to pull in so I'm actually just gonna pull that in right quick. Gotta say, let's call it set, active, song, singular = useStore, or what's it called? UseStoreActions. That takes a state, that's gonna be any, and just like before, it's just gonna return state.set.
[00:03:51] Active song singular, so it's gonna take that. This use effect is also gonna watch that as a value, cuz we'll be using it. And then it's also gonna watch all the songs, so those are the dependencies of this use effect. And then aside this use effect, what we're gonna do is we're gonna say set active song to be songs index.
[00:04:16] That's it, lemme go to my store real quick and make sure I got the right name. It's change active song I call it set, so change active song go back to our layer And Natalie's change active song there there we go. Okay cool so we got the active song set there so let's try to call next on one of these and see what happens.
[00:04:42] So I'm gonna hit next and you can see it moves to the next song now if I go next again to the next song if I go previous and moves back to the previous song. Pretty good so all we're doing is watching that index and we're just saying cool update the active song to be whatever songs of this new index that's it.
[00:05:02] So works pretty good, And then some of the other issues are going to be around when the song attempts to end but let's check out the shuffling and repeat. So, if I put repeat on and I go all the way to the end, let this in. If it works, it should say the same song, let's see what happens.
[00:05:32] It didn't, It didn't say the same song so that's an issue we have to fix. And then also with shuffle, if I hit shuffle on that I hit next. The next song after the Elevator Bossa Nova is Is eternal springtime. So unless it randomly picks that song, which I hope it doesn't, when I hit next, I should not see turtle springtime, I should see something else.
[00:05:52] Again unless it randomly picks a song that we just don't know and I'll try it again. So if I hit next, it went to Fermi paradox. So it looks like the shuffling might be working there, so we hit next again, I would expect it to go to something other than elevator, something hidden there.
[00:06:12] It goes to astronauts if we go here, so it looks like the shuffle is working there pretty well. But the repeat is definitely not working. So if we try that again, just to be sure, this should stay here and It's not. Okay, so before we try to fix the repeat, I wanna just walk through what we have a repeat and see why it's not working to see if anyone can take a guess of why it might be.
[00:06:44] So for the repeat, we have a set repeat call here. It's bounced, it's gonna be set inside the on repeat handler. On repeat handler is going to be set when you click this button here, the repeat icon. And then what happens is on end we check and repeat is true.
[00:07:07] If repeat is true, we just say hey, seek to zero, go back to zero. But for some reason it's not doing that, and in fact, we can throw a log in here. We can say console log, should repeat and we can throw another log in here and say is how or why did you get here.
[00:07:29] Okay so I'm gonna go back I'm going to have repeat on or just refreshes to make sure we're good to go I'll pick a song on my turn repeat on. I'm gonna to go all the way to the end of this console and let's see what we get good because we know repeat is true because this thing is lit up white.
[00:07:50] But as you can see, it logged why did you get here? So even though we know that repeat is true, it's here because that wouldn't be lit of white if it wasn't. But for some reason inside of the on end function repeat is false. So how can a state be two different values at the same time
>> Is like a race condition.
>> Did we accidentally create like a closure of it?
>> Yes, there's a closure. Yes, there's definitely a closure happening here. And what's really happening is because we have to pass that on in callback to react taller.
[00:08:35] So when this component loads up, when we pass the on into it at that time, the on end the repeat is false. So by time it actually calls the on end function. It's calling the one that was passed to it which had a falsie value. But since then the opponent had re rendered so many times because of the request animation frame from you clicking the repeat button.
[00:08:55] That repeat actually changed as far as the latest value. But the callback that react Howler had had on in where the repeat was falsi. So it's stuck in a closure, and therefore it's outdated value. So that's a problem, so this actually happens a lot If you are using hooks that have event callbacks.
[00:09:16] Anytime you got to pass in an event callback into something with the hook, you will run into this closure problem. And there's a lot of ways to get around it, I think react even has a guide on their website that talks about this is the ways you get around it.
[00:09:28] Here's how I get around it. So what we're gonna do is we're going to use a ref. So we're gonna come up here and we're going to make a new ref, just for the repeat. So we're gonna say, repeat or if I can spell, repeat ref, that's gonna equals use ref, like that.
[00:09:49] And it's initial values gonna be whatever the repeat state is. So we're just gonna make a ref and keep it in sync with the repeat state. A reference won't fall victim to the closure because of how it basically opts out of the react rendering cycle. You could think of references like, just like some global variable that acts the same way you think a regular global variable will act.
[00:10:12] Whereas a state variable is being tracked by reacts and it's like using it for rendering, because that's how it renders, whereas you could change a reference and it will never rerender a component. That's the whole point of it, so it's not gonna fall victim to the closure that the state would.
[00:10:26] But we do need the repeat state for the initial so we'll have that. And then the next thing we'll do is we'll set up a another use effects that just keeps the repeat ref and the repeat state in sync. So that way we never have to mess that up so we'll make another use effects here, it's only dependency is gonna be the repeat state.
[00:10:47] All we're gonna do is to say the repeat ref dot current equals repeat date that's it we're just keeping those two in track, And now that we have that Instead of checking if repeat is true inside of our own and we can check if repeat ref.current is true, and that'll always be the latest value.