Jotai for State Management 1 exercises

Implementing Jotai for State Management

After installing Jotai, our first step will be to create the atoms for our state.

Creating Atoms for Cart and Reviews

Create a new atoms.ts file in the src/app/store directory. This file will contain the atoms for cart and reviews.

At the top of the file, import atom from Jotai, as well

Loading solution


00:00 Okay, so the first thing we're going to do is add the Jotai library to our example. All right, now the next thing we're going to do is remove the cart context, and we're going to go create another store directory. This one is going to have our atoms. So we'll bring in atom from Jotai.

00:19 We'll bring in our types for cart and review, and then we'll create our cart atom that has this initial state of just an empty cart. And then the reviews atom that can be either an array of reviews or null, and we'll just start that off at null. Now to create our store that we will initialize on a per request basis over in the layout, we'll create a store provider.

00:39 This is going to be a client component. We'll use useState to track the store. Then we'll bring in createStore as well as provider from Jotai. Jotai comes already with a provider, you just need to give it a store. So we're going to initialize that store using useState, and then wrap

00:55 our children in the Jotai provided provider. Okay, let's go over to our layout and wrap our layout in our new provider. So now we'll go down here to cart provider, and we'll just replace that with store provider. Now who's going to initialize that store? Because that store needs to get initialized somewhere,

01:14 so we're going to do that initialization in the header. So we'll add cart to our header, and then over in header we will say that we have an initial cart, and then we're going to bring in useRef. We're going to use useRef to track whether we have initialized our cart or not. Then we'll bring in useStore and useAtomValue

01:33 from Jotai. useStore gets us access to the store, and useAtomValue gets us access to the value of an atom. There's also useAtom, which returns both the state as well as a setter, and useAtomSetter, I think, to get the setter for an atom if you just want that. All right, so now we're going to

01:52 initialize our store. First we have to get access to the store, then we need to say are we loaded or not, and if we're not loaded then we want to initialize that atom, but we need access to the atom, so let's go bring that in. And then finally down here where we would use cart, we're going to use that atom value

02:11 to get the cart atom value, and then as an option we'll give it the store, and that tells Jotai which store to use to go get that value of that atom. All right, with that out of the way, we can do it a little bit easier over in the cart pop-up. We'll bring in useStore and useAtom from Jotai.

02:28 We'll bring in our cart atom from our atoms, and then to get cart and set cart we're going to call useAtom with that cart atom and with that store. So of course we got to get the store and we'll just call useStore. All right, now add to cart, let's bring

02:47 in useStore and also useSetAtom. It wasn't useAtomSetter, useSetAtomMyBad. So we're going to get set cart from useSetAtom with our cart atom that we need to go get, and then we're going to set that store to the output of useStore.

03:06 Seems to be doing okay. Let's go over and go to the dragon, add the dragon t-shirt to the cart, and away we go. How cool is that? Nice! All right, now one thing to watch out for if we look back in layout. So in here it's important to notice that the header and the main are both siblings of each other,

03:26 and header is doing the work of actually initializing the store. Now in the AppWriter documentation and the React documentation they talk about never relying on the render order of components. We're kind of doing that here except for the fact that the only thing that's over in the page is add to cart, and add to carts

03:45 only be setting that store. So I think in this case that's probably okay. If that wasn't okay, what you could do is probably create another client component that would take children. It itself would do the initialization first, and then any components within that would get rendered, and I think that would get around the problem of the ordering

04:04 of the initialization of the atoms in the store. All right, so now let's do the reviews. We'll go into components, and we're going to do the thing where we initialize in each component. So we'll do initial reviews. We use the useRef to track whether we've been initialized or not, and then we'll get useStore and useAtomValue.

04:23 Then we'll get our reviewsAtom, and then we'll get our initializer code. So we're going to bring in store with useStore. We're going to set the initialize to false so that it never runs again after that first render. Then we're going to set that reviewsAtom with our initial reviews, say that we're already done, and then

04:41 after that we're going to get the atom value for the reviews. After that's really important, if you get the atom value first, then you won't get the initial value. You'll get a copy of the original value of reviews, which is probably not what you want. All right, let's finish up by implementing on reviews. Again, we'll bring in useRef, useStore, and useAtom,

05:00 as well as the reviewsAtom. Again, we're going to call this initialReviews, and we're going to do our initialization. This is exactly the same as we had before, but of course we need call set reviews. So let's go do that down here. Okay, looks pretty good. Let's give it a try.

05:21 Cool dragon shirt. One star. Not very good. All right, let's give it a try. Let's go and see that cool dragon shirt is in our SSR output. And there you go, a complete Jotai example of this port server-side rendering that shows you how to manage atoms

05:42 both at the layout level as well as the per route level. A nice example to work off of, and an interesting state manager that you might want to take a look at for your next AppWriter application.