Maximilian: Now, as a final step in this application,
I wanna come back to this button in the header,
to this HeaderCartButton.
Everything works in this application,
but I wanna play a little animation on this button
whenever we add an item to the cart,
or also when we remove an item,
so whenever the amount of items in the cart changes.
And for this, I already prepared something in the CSS file
which I provided to you earlier.
I set up a little CSS animation here
which makes the button bump and change its scale.
Now, we just need to make sure that this class,
which starts this animation,
this CSS class is added to the button.
And for this, here I'll add a little const btnClasses,
and I'll simply set this equal to a string
using the template literal notation with back ticks
instead of single quotes.
And here, I will always add classes.button.
But then, conditionally, I wanna add the animation class.
Now, to show you how it works, I will add it right away,
not conditionally but always for the moment.
And that's the bump class.
And now, here, for a class name on the button,
I set this equal to btnClasses.
If you save that and reload,
you'll see this bump animation here.
It only plays one, but later it should play
whenever this cart changes.
To make that happen, we can utilize useEffect.
We haven't used it thus far in this application
because we had no traditional side effect up to this point,
no local storage access, nothing like this.
Of course, you could add such features,
but here we simply don't have it.
Therefore, here I will utilize useEffect
to play this animation.
And therefore, here in the HeaderCartButton,
I'll import useEffect,
and then call useEffect here in this component function.
Now, we pass our effect function to that
and then our array of dependencies as you learned.
Now here, in this effect function,
I wanna do one simple thing in the end.
I wanna change my btnClasses
to include the bump animation class,
and then I wanna set a timer which removes that class again
so that when it's added again in the future, it again plays.
So therefore, I will also import useState
because we'll need to reevaluate
and re-render this component
when that animation class is added conditionally.
So here, I'll call useState,
set this to false initially because this will control
whether or not the button should be animated,
and I'll name it btnIsHighlighted,
and setBtnIsHighlighted.
Of course, the state names are up to you.
Here, in useEffect, I'll then call setBtnIsHighlighted
and set this to true.
Then, as a next step, here for my btnClasses,
I will not always add the bump class,
but I'll check if btnIsHighlighted is true.
And if that's the case, classes.bump is added,
otherwise an empty string is added,
so no extra CSS class is added then.
And I do this here because, since I'm updating the state,
the whole component will re-execute when that state changes.
So this line will also change again,
and the updated classes will be picked up down there.
Now, of course, I don't always
wanna change the state though.
I only wanna change it if my items changed
and if the length is greater than zero,
so if there is at least one item in the cart, let's say.
Hence, here I will check if cartCtx.items.length
is equal to zero, in which case here I'll just return
so that the rest of this effect function doesn't execute.
Because this should only execute
if we have at least one item in the cart, let's say.
Now, we need to add this as a dependency.
But if I add cart context like this,
this effect would rerun whenever anything
about my cart context changes.
And that's not what I want here.
Therefore, I'll use object de-structuring
to pull out the items, to get them out of my cart
so that here I can just refer to items
and pass items as a dependency,
so that not the entire context, but only the items array
is a dependency for this effect function.
Of course, we can also pull this out a little bit earlier.
And, for example, also then refer to just items here.
Now, if we save this, if I reload,
if I click add, watch the button in the top-right corner.
It now bumps, but only the first time I add something.
The reason for that is that then this class is added,
but since it's currently never removed,
thereafter, it never changes
because the animation is actually only played
when this class is added for the first time.
Therefore, we should also remove the class
after the animation finished.
And I'll do that by setting a timer here.
I'll set a timer which fires after 300 milliseconds
because that's the duration of this animation here.
I played over 300 milliseconds,
so after that duration, I trigger a function
where I set btnIsHighlighted back to false.
And if that's false,
this here will ensure that an empty string is added
to the classes string instead of the bump class.
So that will remove that CSS class from the render to DOM.
Now, I also wanna add a cleanup function
where I clear that timer
in case that component should be removed.
This can't happen in this application,
the button is always there,
but it's still a good practice to clean up any timers
or any other side effects that might be ongoing
because you started them in useEffect.
So here, I'll return my cleanup function.
You learned that if you return a function in useEffect,
this will be called automatically
as a cleanup function by React.
And here, I'll call clearTimeout and clear that timer,
so that when this effect reruns, we clear the timer.
And actually we do need this here now that I think about it
because, of course, this timer can be set again
before it expired.
If we add multiple items rapidly after each other,
we wanna clear the old timer
and make sure that a new timer is set
and the old timer is, well, cleared.
So we definitely should have that cleanup function here.
And now we do.
And with that, if we save this, if I reload,
you see, whenever I add an item, this button bumps.
And that's just a little visual tweak,
and in addition, as a nice side effect, no pun intended,
it's another practice and exercise of useEffect,
which you need for handling side effects.