Transcript from the "Sharing State with nanostores" Lesson
[00:00:27] And that becomes available to all the components in a way that's sort of built into the framework. Now in Astro, because we're individually hydrating these pieces, we can't put the store in a provider and then just grab out of the way you would and react with a use X hook, right?
[00:00:46] So instead we have to figure out a way to keep the store external to the components themselves, but still be usable and updatable within the components. So that might sound I was very intimidating to me when I first thought about this cuz I was like, there's no way this is gonna be, this is gonna be a mess.
[00:01:04] This was honestly the first part where I was like, well, this is where Astra is gonna fall down, right? And then I got this wonderful, wonderful demo. I'm not gonna show the demo, but I'm going to give a link to the demo. Because it was absolutely wonderful. Ari and Diora came on learn with JSON and did this demo of sharing state between multiple frameworks in Astro.
[00:01:35] It's so good. If you wanna see a perfectly executed demo, watch this. The TanStack team, Arian and then I had Dominic, TK Dodo on the show as well. Absolutely outstanding, both times, just incredible, incredible demos. Let me find Dominic's as well, this one here. This is a team that makes wonderful demos.
[00:02:00] So good, good, good people to check out. And this particular demo is where I realized, wait, Astro can actually do all these things that I was really worried it couldn't do. So we're gonna use a riff on what Ariane showed using a tool called Nano Stores to share state between our solid cart and our React add to cart button.
[00:02:27] So to do that, we first, well, let's talk a little bit about why Nano Stores because there are a lot of things we could use. A lot of different state management things are out there. We could try to put it in local storage. We could try to put it in session storage.
[00:02:44] We could store some, I mean, maybe we could put it in cookies. Who knows? We Could reach for 10 stack because the 10 sec query is framework agnostic now. We could reach for a handful of other tools that I've seen out there that are all very good in different use cases.
[00:02:58] What I like about Nano Stores and the reason that I'm reaching for it now, and let me pull up the page for it, I guess while I'm talking about it. So, Nano Stores is 313 Bytes. It does exactly one thing, and it does it very well, and it's intentionally simple.
[00:03:19] And for the use case that we have, which is to keep track of one array, this is the right tool for the job. We don't need any of the advanced query invalidation. We don't need syncing between states. There's nothing intense happening here. We just need a list, right?
[00:03:37] And for that, and for a lot of use cases, really if you break down state to what it actually is, it's usually just one value, a list of values. And a lot of times, we don't need the extra features. So my general working decision process here is I'll look and see, can I do this in Nano Stores?
[00:03:57] And if I find myself writing a lot of custom code to make Nano Stores do it, I'll reach for tanstackquery. Because tanstackquery is great when you need things like cache invalidation and query sharing and all these really advanced pieces. The most of the state that I'm creating does just doesn't do that, it's not really necessary.
[00:04:16] So again, choose the right tool for the job and you're gonna have a lot less maintenance to do later. So, Nano Stores is wonderful for this. It has its teeny tiny, it has a really small API, and the only real trade off of using Nano Stores Is that as part of not shipping a lot of code, they don't have things that might seem intuitive like a delete option.
[00:04:41] You have to set a key to undefined to delete it, right? So there's a little bit of like, that was a weird choice. But when you think about it 313 bytes is, you don't have a lot of room, you add a delete method and suddenly your library's doubled in size, right?
[00:04:54] [LAUGH] So it's very much like they chose sparseness over size. Which makes it a great decision for this very simple use case that we are doing today. So to actually use it, we're going to head over here and I'm gonna open up my terminal cuz we're gonna install something.
[00:05:16] So I'm going to install NPM, install Nano Stores. And then we're going to need both the React and solid adopters for Nano Stores. And it didn't do something. I'm using the wrong version of node. I meant to be using 18. Okay, so now that I'm using node 18, I installed Nano Stores.
[00:05:52] We've got the packages here and they're available for us to use. And we are now able to create our store. So let me open this up. I'm gonna create a new folder in here called stores. And then I'm gonna create a new file. And we're going to call it cart.ts.
[00:06:10] And the way that a cart works, it's just a list. All we need is a list of the things that are in our cart and a quantity of how many of those things are there. So I'm gonna grab two pieces computed which we're not gonna use just yet, we'll look at that in just a second, and map, which is the thing that we need right now.
[00:06:30] So, Nano Stores has, I think, two or three baseline primitives. And the first one is an atom, which is like a value, like what you would do with useState. So you set an atom to a value of 0 and that's your counter, right? And then you can also use a map.
[00:06:56] And a map is an object more or less. That's really kind of the whole thing. They've got an option for a deep map if you wanna do a little bit more, but through the vast majority of use cases I've run into you can get away with a map or an atom, and that's about all you need.
[00:07:15] So we're going to set up our cart. So we're gonna export a const called cart and that is going to actually add a store started prefixing everything with $ sign, so we're gonna do that so keep with if you look at their docs you'll see that everywhere. And then we're gonna set up a map with an empty object.
[00:07:35] Now, I want auto-complete, so I'm gonna write some really gnarly typescript. Feel free to just skip this, you can just write any here if you want. But I'm gonna set this up as a record, and then inside of it, the key is a number, and a cart item, which is defined already, is available there.
[00:07:58] What did I do? I'm missing a closing bracket, there we go. Okay, so what we're saying here is that our map consists of a record that includes a number as a key and a cart item as a value and that'll give us autocomplete. The reason I'm using a number as a key is that the cart items that we have are coming in with a numeric ID, just starting at zero and counting.
[00:08:19] And that's it. That's the whole store. We'll be able to function from there. So, to add items to the cart, we could define this elsewhere, but we're just gonna put it all on this file, and make it so that we're exporting utilities right there. So, I'm going to export a function called add item to cart, and this is going to accept an item, which is a shop item.
[00:08:44] And that's gonna be coming outta the API, so that we've got the ability to, again, we just want auto complete to work, so, I want a cart item const and that is going to be, we're gonna use the cart itself. And we're gonna try to get our current item, so we're checking if that item is already in our cart.
[00:09:07] And I need to add my dollar sign because I did that. So we're checking the store to see if we already have this particular item in the cart. And the reason we're doing that is because if we already have it, we don't wanna add it again, we wanna update its quantity.
[00:09:23] Next, we're gonna set the quantity of items. And we do that by checking to see if our cart item exists. If so, we're gonna say the quantity is the existing quantity, otherwise, we'll set it to 0. Then we're going to run Scart, setKey and we pass in our item.id as the key.
[00:09:49] And then it's gonna let us auto complete here. Or it's not. We're gonna set an item. And then we're gonna set in the quantity to be the existing quantity plus one. So, this way, we have either a new item gets added with the quantity of one. Or we override the original item that has the the key of the item ID with a new quantity of whatever the original quantity is plus one.
[00:10:23] So as we add, we're saying, give me one, give me one more, give me one more, and we're not ending up with a list on the side of like the same product a bunch of times. And that is the whole function to add item to cart. And then we wanna do the same thing for removing.
[00:10:40] So we're gonna export a function called removeItemFromCart. And that one is going to get just an item ID is all we need. So that's a number and then we're going to tell it to ts ignore it. Because we're going to set the value to undefined and rather than wrestle with this record to not yell when I set it to undefined, we're just gonna, we know that we're deleting the key, right?
[00:11:08] So I'm going to cart, nope, right, there's a dollar sign cart, set key, and then we set the item ID and undefined. And that will remove that item from the cart, allowing us to now add and remove things from our cart. Okay, so what else would we need in a cart?
[00:11:32] The only other thing that we'll need in this particular case is going to be a subtotal. We wanna compute how much the items add up to in your cart for total price. So we will export something called subtotal. And this is where we're gonna use that computed thing.
[00:11:51] So what computed means is we're going to compute an additional store based on the value of a different store. And in our case, what we wanna do is take the cart and based on its values, we want to give a new value. We're coming up with something derived from the scart store.
[00:12:13] And the way this works is we say computed then we pass in the store that we want to use as the trigger source. So whenever the cart changes, we want this subtotal to recalculate. That's how we're identifying this here. So we take the cart and then we get the entry.
[00:12:28] So whatever's in the cart right now or in the store right now, we can then do some math. So we're gonna take our subtotal, start it at 0, and then I'm going to get the values out of our entries. So because it's an object, right? We've got the ID and then the items, but I wanna loop over it.
[00:12:48] So I'm just gonna get the values, which just gives me my list of items. So I'm gonna get my entries and then for each, I'm gonna get an entry. And I want to make sure it exists. So if it's not there, we can bail on it and just return, we'll skip that one entirely.
[00:13:07] Otherwise, we're going to increment the subtotal by the entry quantity multiplied by the entry item price. And so this is rough math, we're not calculating taxes, we're not doing discounts. So there's a lot more complexity that can and probably will in a high traffic web store. But for the defaults, if you just wanna get something off the ground, this is pretty great.
[00:13:39] As far as simplicity goes, and then at the end, we just wanna return that subtotal. And so now, we have the ability to get our cart. We can add an item to it, we can remove an item to it, and we can figure out how much the total cost of the cart is.
[00:13:54] And that is all the dynamic functionality that we should need in terms of interacting with our cart.