Practical Svelte 5 - Shopping Cart

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what's up everybody today we're going to be learning about or taking a look at spel 5 now that it's in its release candidate stage and I feel much more confident that the apis are stable I want to make some videos on it so the goal here this is something new for my channel I don't think I've done something like this before but what I want to do is I want to to start off with some basic markup here and implement the functionality needed at least the UI part the front end part we're not going full stack here needed to make this web page functional so if we look here we just have a bunch of products it looks like a lot of makeup I know I'm using a dummy Json API to get all this product data but we can see here that we're just rendering out a bunch of different products we have a cart TI here with a you know placeholder product the ability to increment and decrement quantities here remove so we have a bunch of different features here that we can actually Implement all of which will rely on spel 5's new apis the reactivity apis so I thought this would be a cool little challenge if you want to just go ahead and do it yourself I'll leave a link to the starting code down at the bottom um but if you want to see how I would handle some of these things feel free to hang out all right so let's take a look at what code we're starting out with here again this is mostly the only part of JavaScript that's in here is this each block in felt that we're iterating over the products in the rest of this is just tailwind and HTML a couple different icons nothing too fancy but I didn't want to bore you with trying to build all this out and make it look half decent uh during this video as I want to focus on the reactivity piece the JavaScript piece the spel piece so just to give a quick tour we have a page. TS file where we're loading data from this dummy Json API we're then returning those products here from the load function now I've went ahead and made a type for the product I know it looks really complex that's just what gets return back from the API we're not actually going to use all of these however we might in a future video do some things with tags or filters and stuff like that but for this video we're just focusing on a couple of these properties so don't look too far into this at this point in time so in this page here we're rendering out you know the shopping cart button rendering out the shopping cart itself we have this cart item component which is that item up here within the card itself so we're looking at how do we pass props into components in spel 5 um and then also being able to you know handle 2A binding within each blocks and things like that and then we also have the actual core content of the products which is just a grid rendering at each product with its thumbnail title price and then and add to cart button here and so that's what makes up this entire page now our goal is to add functionality to this page and I think the first thing that I want to do is get this cart out of my way so the cart shouldn't just be open by default we should be able to close it and open it toggle it you can see here we have this x so we can actually click that to close it but I think having the button toggle it so if it's open the button would close it and vice versa would be a nice place to start the first thing I'm going to do is I'm going to set up uh some State here here so I'm going to start with an cart open state so we'll say cart open is going to be State and by default it's going to be false now here's the actual button that opens the cart and so we'll kind of wrap the rest of this stuff within a conditional so we'll say if cart open then we want to show this if not then we don't okay now if we check this out here we'll see that we no longer actually see our cart so on this button We'll add an onclick Handler to set cart open to the opposite of whatever it is at this point in time so we'll just toggle it like so okay so now we can actually toggle the cart and it all works great we might as well while we're here go ahead and take care of that x button here so we can um me be efficient here and say close cart and then have an on click Handler where we set cart open to false in this case we're not going to toggle it because of the fact that that's an X right that's Clos cart toggle cart okay so this is coming along nicely right we can see how the dollar sign State Rune is able to react we can simply just update that and our UI will reflect and so the next thing we're going to do here is start to actually add these products to a cart okay and to do that we're going need to have some type of list an array of products that are actually in the cart so we can say cart products and this is going to be State and it's going to be an array now the type for this we could of course just update the product and add a quantity property to it but in a realistic scenario here we'd probably be getting this from our own database we want to keep that pure and then instead just have it as a property on a different object so we'll have the type for cart product and we should actually put this into our types file because we're going to reuse it in the cart item component so we'll just call this cart product and it's going to have a product it's going to have a Quant and then it's also going to have an ID and we're going to leverage that ID to be able to actually remove this specific cart product from our cart and we're just going to generate this on the client side nothing too crazy we could also use the products ID but again I just want to kind of keep the cart product separate from the C from the product itself right we just have product attached to this entity here okay so we'll save that and then now we can come back in here and import that type and so now we have a reactive array of cart products I need to import this as a type okay so now down here inside of our products that we're rendering out we have this add to cart button so what we'll do here is We'll add an onclick Handler and we'll say cart products. push and we're going to push an ID which going to be crypto do random uid here just give us a random ID we'll have quantity of one initially and then we'll also add the product itself into that object okay so now we are just pushing these products into the cart products once the user clicks on the add to cart button so now what we have to do is we're only rendering One cart item at this point in time and I used a component here on purpose I'd like to cover Snippets in a future video potentially replacing this with a snippet but I do want to show this component Behavior as well in spelt 5 because it is slightly different than spel 4 and you might run into some things that are kind of confusing so hopefully I can clarify some of that for you all today so right now we're only rendering that one you know dummy product here so let's just go ahead and say each cart items or each what what do we call it cart products each cart products as cart product and if we pass in cart product like this it's going to give us an error here because we're not actually accepting any props but if we go ahead and test this out here we add to cart add to cart we're going to see that we are in fact rendering one of these dummy products for every item that we add to the cart so that part is working so we are actually adding things to this cart products array and our UI is updating to reflect that so now we can go into this cart item component and we can set up some props so with spel 5 I'm sure most of you might know if not you know you declare props with let and then dollar sign props like this so you have an object you destructure and then use the props Rune to signal to the compiler hey these are my props now you can type them by adding the type here I know some people prefer to do it in line like this where we'd say c product but I always end up needing to move it into a separate type because it gets too messy so I just end up doing this setting the type up here or another file if I'm going to be exporting it elsewhere and I'll say cart product is going to be a cart product and then here we'll type this as props and then we'll pull cart product out of those props okay so now that we have access to this cart product we can start referencing it inside of the cart item I probably should have came up better names for this again we're probably going replacing this with a snippet in the future maybe in a future video but for now we're just going to say cart product. product. tile because remember we have that product property that's holding the product of this cart product I guess it could be cart entry you guys get the point here and then this is going to be cart product. product. price and then this is going to be the quantity that we have in the cart so this two here and then this is the minus button and this is the plus button that's how we're going to increment and decrement the number of this specific product that we have in our cart from the cart itself then we also have the trash icon here or trash button which allows us to remove something from the cart okay so for this quantity we're just going to set this to cart product. quantity just like so and then for this Source here we'll set this to cart product. product. thumbnail so it's using that same image that we see on the actual card itself and I think that's everything that we have to do here so now when we come back to the page if we refresh we shouldn't have the cart open it shouldn't be showing two there we'll fix that here in a second but if we start adding some things to the cart add a couple colog some nail polish maybe add a a fish Stak head back up we're going to see that we do in fact have all those items we added to the carts being rendered within our cart okay so now let's go ahead and fix this quantity and then we're going to look at how we can go about incrementing and decrementing the count of each one of these items with cart from within that component and that's why I'm using a component here because it's a bit different and I want to make sure that I cover that in this video so back on our main page here we have that two if we think about it I'm not too sure if it should be the number of top level SKS or whatever that we have in this cart or if it should be the total of individual items so if we had four items or five items here and we added two here it would actually be six so maybe we'll do that so we'll set up another active value this one is going to be a derived so we'll say a cart quantity and we'll have it be derived and we're actually going to do derived. BU because we're actually not going to use a reducer because reducers are really slow and I always feel bad when I use them and we're basically going to iterate over each of the cart products and add up their quantities right so we'll have let total equal zero and then four con product of cart products we're going to add one to the product quantity and then return that total so cart total now or cart quantity is derived from the cart products so every time we update cart products it's going to rerun this this function here and update cart quantity and then the UI that depends on that will also reflect so here if we instead replace this two with cart quantity then we go back we're going to see that we're at zero and probably better ux would be just not show anything there and then show it but for now we're fine so let's start adding some things to the cart we'll see that it does in fact react to those updates Okay cool so now what we want to do is be able to actually increment and decrement the quantities within the card itself and so your first instinct might be to come into the cart item component here we'll grab this button we'll say onclick this is the plus button so unclick cart product. quantity plus plus and then for the minus button we'll do the same thing accept subtract right we have a reference to the object everything would make sense that this would be okay to do so let's refresh and get a fresh start We'll add a couple things to the cart and then we'll start incrementing those items and sure enough it does in fact work but if we check our console here we're going to see spelt is not very happy with us so for one I'm using the wrong fetch my load function it doesn't matter in this specific case but I should update that but here's the one I care about is ownership inv valid mutation so it's telling us that card item. spelt mutate a value owned by the page this is strongly discouraged consider passing values to CH components with bind or use a call back instead now the reason for this issue or the reason for this message I've had my own personal beef with it too so maybe it's confusing to you as well but ultimately now that we're using signals and these reactive objects that can just be passed as prop that means any Library any component Library anything could just update your states without you explicitly telling them that they can do so right because this is a deeply reactive array of heart products which are all proxy objects at the moment there's no way for us to say hey like don't mutate this or hey this is only read only right because it only exists in one place so spel forces us to use the bind keyword to say hey I'm allowing cart item to mutate cart product on my behalf and update the state here in this plus page so in state that the page owns I'm allowing cart item to update so your instinct might be after seeing that message to come down here and say bind whoops bind cart products okay shut up be done with it but unfortunately we get another little error here that's letting us know we cannot reassign or bind each block argument in runes mode now why this is I'm not 100% sure I'm sure there's a perfectly valid and really difficult technical problem that forces this to be the case because otherwise they would clearly want to do this uh but in this situation here we actually have to kind of do a workaround so we have to use the index of this array rather than using this cart product itself so to do that I'm just going to use an underscore here to say hey we're not using the cart product from this part of the array but I do want the index okay and so now I'm going to bind cart product equals cart products at that specific index and now we're going to see another little Squigly and that's because we haven't actually told the card item component that this prop is bindable and so we're being more explicit here which I actually prefer so we have to come through here and say bindable and that lets us know that's how we can indicate to Consumers of this component that hey you can bind to this prop or hey you can't bind to this prop if you do we're not going to do anything with it and so that's that's how we can indicate that here is we use this bindable Rune like so so now all of our little errors have went away we come back into our browser refresh the page I'm getting that still getting that message about the fetch let me just fix that so it doesn't pollute our console now we'll refresh we don't get any error messages here let's go ahead and try to add some things to the cart we'll go back into our cart and then we'll mutate them and sure enough it is in fact reflecting the State updates because remember the cart quantity is being computed in the page and we're mutating these down in the cart item so it is propagating those State changes up and we're not getting any error messages which is great okay so that covers the Bind part now if you me if you remember that airor message said something about call back right what are they referring to they're referring to basically passing a function down that is in charge of updating the state so you declare that function here within this component that owns that state and you pass that function down to be invoked but all the state mutation is still happening within this component and the perfect time to demonstrate that is now because we want to actually delete this item from this cart products array right and right now cart item doesn't have a reference to the array itself it just has a reference to that specific object in the array and so we'll need to leverage a call back here we can just simply set up a function called remove item remove from cart it's going to take in an ID which can be a string and then we'll set products equal to cart products. filter uh product and so basically we're just going to filter out that single product from the array whenever this function is called okay and so now for this cart item to actually be able to use this we need to pass it as a prop so let's go back into our C cart item and set up a remove item function it's should take in an ID of a string and it's going to return nothing now in this case this doesn't need to be bindable because it's not mutating any state now technically the result of this function will actually mutate state but the mutation is happening in the owner of the state component which is this page hope that makes sense I don't not trying to be too confusing here but I want to make sure I explain it in a way that that isn't treating you like a little kid okay so remove item we have this function now so on our button here trash button we'll say onclick remove item and we'll pass in product or cart products. ID okay so now we have this call back being executed here now you could have done the same thing with the update rather than going through the process of binding we could have just said update item give it an ID and then also like the new item so when you call that function it would then update the item in that array on your behalf but there's a couple different ways you can do this they give you the flexibility to do so so our linter is yelling at us now because we haven't passed that function down so let's go ahead and do that remove item equals remove from carts okay so let's go ahead and test this out see if everything works we're going to add a few things to the cart we'll come in here and then we're simply going to delete it and it's going to remove it from the cart now another instance when we want to remove it from the cart is if they knock the quantity down to zero at least in my head that means like hey I don't want this I suppose we could also disable this button but let's just be cool here when they drop it to zero we're just going to drop it from the cart in our cart item component rather than just straight up minus minusing here we're going to run a conditional so if cart product. quantity. quantity is equal to one then we're going to remove item cart product. ID else car product. quantity minus minus okay so if the cart product quantity is currently at one and they're trying to decrement it that means they're going to zero which means we're going to remove the item from the cart otherwise we're just going to do like we would normally do and drop one from the quantity so now if we head back over here we refresh the page we add a few different things to the cart and we uh remove this one we'll see that it does in fact just disappear I'm not too sure if that's great ux or not um but it is functional so now we increment we decrement we can delete everything is updating except the total so we haven't gotten to that part yet so let's go ahead and get to that and that's going to be in our plus page so if we think about what we need for the total we basically need every product that we have in our car products we need to multiply the price of that product by the quantity of that product and then add all those up and so we could of course set up another derived statement here and iterate over the products again but since we're already iterating over the products anyways and we're already looping through each one of them and summing up the total we might as well just get both values from this single within this single deriv so we'll rename this to cart stats and we'll have quantity and then we'll also have total I guess right so total and quantity so quantity is going to be this total is going to be we're going to add the price times that product's quantity just like cop is suggesting here to the total and then we'll just return quantity and total just like this okay so now we can go down here wherever we were using card stats so for the total we'll make this quantity I'm not really too sure about the total price maybe I should call it total price but for now we're just going to leave it at total and then for total here we'll say cart stats. total now if we go ahead and increment these counts we're going to see that our total is in fact working and if we open up the cart we scroll down a bit we're going see that we're getting some really long number and we can fix that really really easily by just adding two fixed to the end of this with two that'll give us just two decimal place number and so now if we add some stuff to the cart check it out we're going to get 17796 we remove something we're going to see that it is in fact updating all of these various states are all staying in sync that's really the power of the derived so the next thing I want to cover today is effect and we're going to talk about it just briefly here but I'm going to show you an example of a good way to use an effect and an example of a bad way to use an effect so effect is for running side effects in response to some other reactive state right so it does automatic dependency tracking allows you to opt out of dependency tracking altogether and a lot of cool stuff but it's really easy to run into traps with it if you're using it the wrong way so let's say in this case a side effect that we want to show is that if the user gets the over $50 in our cart we want to show an alert saying hey you qualify for free shipping okay so you might think let's just go in here and say effect and we'll say if cart stats total is greater than or equal to 50 we want to show an alert that says you have qualified for free shipping this seems completely reasonable so now let's go back into our application and let's get to over $50 so add this for $49.99 we add this we now have over $50 and we got that alert now the problem right now is that uhoh I don't want this to keep happening I just want it to happen one time right so I want it to be a onetime effect that only runs when they hit over $50 if they drop below 50 we want it to re-trigger again when they go back above 50 now again this probably wouldn't be an alert maybe it's a toast or something else that you have somewhere else on the screen and maybe you could handle this with the derived and and conditional rendering but in this case we're actually talking about a side effect we want to produce here and so the problem here is that it's this effect is tracking the card stats. tootal which is changing every single time we update or add something to the cart so instead if we head here and say qualifies for free shipping and we make this a derive this is what I'm talking about when I say that you should be reaching for derived a lot more than you reach for an effect so in this case we'll say card stats total is greater than or equal to 50 Okay now what's really cool about this is that now it's tracking this value right so once we go above 50 that value doesn't change anymore even if we add more stuff it's going to reevaluate this expression it didn't change so therefore it's not going to re-trigger the effect so if we come back down here and we say qualifies for free shipping now we head back into our application we add a couple things we're going to get the message once but then as we add things to the cart we're not going to keep getting that alert over and over again because rather than tracking do total of the card stats object it's tracking this and since this evu is to True once and hasn't changed it knows that and it's not going to keep on reiring this effect now if we were to go ahead and delete a bunch of things from the cart and then read them you'll see that since that state did change since that we since we stopped qualifying for free shipping and then we qualified again of course it's going to re-trigger the side effect that's the expected Behavior if you want it to only happen once you could just have let um free free shipping alert counts equal Z and then once we have the alert we'll say free shipping alert count Plus+ and then only trigger this effect if free shipping alert count is truthy or if it's over zero you could do if it's greater than zero we're going to return be specific here and so now once we go over it we're going to get it once but then once we go back under 50 and then go back above 50 it's not going to re-trigger it so these are things to think about when you're working with effects is look at what your effect depends on make sure that is like the most precise thing that it could depend on the more broad you are the more difficult it's going to be to track down what's causing this thing to keep re-triggering over and over again okay so I think that's going to conclude this specific video I think we added all the functionality that we want to add for this beginning step if you all like this video I'd love to make additional videos on this same kind of concept here we can add filters we can add search we can add pagination there's a lot of things that we can do with this and I would love to kind of keep expanding on this nice B L that we have here uh but if you all are interested in that let me know in the comments down below I'm more than happy to do so thank you all so much for tuning in and watching I hope that you learn some today and I will see you in the next one
Info
Channel: Huntabyte
Views: 10,854
Rating: undefined out of 5
Keywords: svelte 5, svelte 5 runes, svelte 5 basics, svelte 5 state, svelte 5 effect, svelte 5 signals, svelte signals, svelte runes, svelte new api, svelte shopping cart, svelte commerce
Id: geAcAzheu_Y
Channel Id: undefined
Length: 25min 9sec (1509 seconds)
Published: Sat Jun 22 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.