State Management for NextJS 13 Server and Client Components

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
You want to use Redux in your NextJS 13 application that uses the app directory? Well, you've come to the right place. This tiny but mighty little Pokemon application that we're going to build is going to show us how to do server side rendering, how to move the store that's in a server into the client, and how to do our RTK query with the new API standard that’s in the app director. It's all here. I'm so excited to get right into it. But before we do that, I do want to say that we have a sponsor for this week and that Scribe. More about them in a bit. Let's get right into it. All right. This is the app that we're going to build. We have a database of Pokemon on an API endpoint that we can then use dynamically to just search. There's also a pure SSR route that shows you exactly how to do Redux, but only in the SSR context. And there is the API route that gives us back the list of Pokemon. And then when we do a question mark, name, it filters down to just the Pokemon that we want. This is the application we're going to build. It is not a full CRUD application, but I don't think you need that. What we’re really looking to explore. here is how to use the Redux store, which you're probably already familiar with in the context of Next 13 with that app directory, with the new API structure and how to do it in both the client side context, which we're very familiar with, but also the server side context, which we're not so familiar with and how to bridge the gap between those two. Now let's go and start building our Next JS 13 application that uses Redux to create this cool Pokemon search page. We're going to use NPX to do create next app with the latest, which is going to give us NextJS 13 at the latest build. We're going to call that NextJS 13 Pokemon and we're going to opt for the experimental app. We want to use TypeScript, we want to use eslint, we do want to use that src directory and this import alias is really nice. You use @ and @ is the src directory. So you can just do @ and then slash and then whatever you have in your source directory. All right, let's bring it up. Now, I did mention that this week's sponsor is Scribe. I'm going to use them right now to document how to start up our application. So I'm going to go over here to the Scribe desktop application. It start recording, and then we're bringing up our terminal and then I'm going to do pnpm to make sure that we're all installed and then I'm going to do pnpm run dev to run it. Then I'm going to click on localhost. This is our NextJS 13 starter. And for documentation, I want to show folks that you can click on the docs link and get the beta docs or you can click on the templates link and get some sweet looking templates for NextJS. Or you can click on deploy and it will walk you through how to deploy this on Vercel. Seems like a good place to end. So I'm going to stop recording on my Scribe Desktop and then Scribe brings up the workflow that I just did to make it super easy to document. You can see that it captured right at the beginning that I did control back-tick to it to bring up that console. We have our image for the console that I can then actually go in and edit. Check this out. I can go and go down and kind of crop it to where I need it to be. How cool is that? Think about how easy this is going to make your documentation and it tracks every single thing that I did as I transitioned in the browser. You can see that I went to my page and then I went to the docs and then I went over to the templates and it's all just there, all ready to go. All you need to do is fill in the text. It's really cool. Now that we're done editing and click the done editing button and we can see that we are basically live with this. In fact, we can go and share it. And there's so many ways to share. We could export this as markdown and just put it off in a dev.to article. How cool is that? Or we could just share it directly with our team by just copying that link and then pasting it into, for example, the README of this project. Or if I wanted to put it in the team documentation, I can just paste a link to it in there. It's great. If you're as excited as I am about Scribe and adding it to your documentation workflow, be sure to go to scribe.how/jackherrington-march. There's a link to that in the description right down below and you'll be helping out not just yourself when it comes to building great documentation, but also be helping out this channel which I fully support. Okay, let's get back into the action. So fundamentally we need an API to talk to. We're going to use our Pokemon data set for that and that's going to allow us to test fetching that data both off the server and off the client. So let's go build out our API for our Pokemon. So if we look at our directories, we have source app now in there, we've got API, it's got a basic root that were just returns hello next to us from the get. You use different verbs now and uppercase get is for GET requests. So we're going to go and bring in some pokemon into the src directory. This is Pokémon JSON. It has a list of all of our pokemon. Again, all of this code is available to you in GitHub in the link in the description right down below. Let's go and bring that into a new route that we're going to build called Search. So in API, we want to build a new folder called Search and then in there a new file called route.tsx. So that's going to give us slash API slash search and anything going to that endpoint is going to go to this file first thing we want to do or bring in next response that's going to allow us to process the response and send one back when we want to bring in our Pokemon and we're going to use that nifty slash because that is now relative to anything in source. All right, let's create a GET request and we'll get the search parameters from the URL. It no longer does the URL query passing for us, but it's no big deal. We just knew your URL and get that from that. We're going to get the name and then we're going to filter down our Pokemon based on that name. So going to do slash API slash search question mark, name equals, and then the name that gives us back our Pokémon data. And then we can simply just return a next response for the JSON with that data. But I don't want to overwhelm the client, so I'm just going to use the top ten on the slice. So whatever we match, I'm just going to use the top ten from that. Okay, let's start to build out a UI on this thing. So I'll go over to the page directory that's just the home page and I'm going to filter all of this down. I mean, for sure it's a beautiful page, but we don't need all of it and I'm going to make a request to that endpoint using fetch. But of course we're going to get a little issue here because we're using a weight and this isn't an async function. And that's the great thing about NextJS 13 in this app directory, when it comes to server side, only components like this one, you can use an async function as opposed to using a synchronous function like we normally do. Okay, now that we should have our data there. So it's just JSON string of client and so we get there we go. Looks good. Kind of garbage. Let's go change up the CSS a little bit. So over here in global CSS, so I'm just going to get rid of a lot of that and just go with a font family of Arial Helvetica whatever and decently sized font. So now we've got our data. Let's format it up a little bit nicer. First, we need to define in TypeScript what the topology of a Pokemon is. So let's go build a new file called types and then I'll drop in an interface for Pokemon. Got an ID number, name type, all the good stuff that you want to know from pokemon if you want to go and extend our cool app now that we know what a pokemon is, I want to go and have a nice little pokemon table that just formats it nicely. So let's go and build a components directory within source and then within that components directory a pokemon table. First, we're bringing in our Pokemon definition and then we use it in our component definition where we say the Pokemon table has a list of Pokemon an array of them, and then this is just going to build out a simple table. It's got a header, it's got the pokemon and the name and you can add on to this. If you want to try this out, I'm going to go over to page.tsx and we'll import it, see how nice that looks and it automatically hinted us to where it was. So these Pokemon table there and just give it our list of data. Okay, looks good. So why would we want to bring Redux into this? Well, let's say that we have a big component tree in this case. We're going to create a new component called SSR Pokemon Table and we don't want to prop drill the data from the page down to this Pokemon table. So how are we going to do that? We're going to use the Redux store for that. So first off, let's go and build our Pokemon SSR component. We'll call it SSR, Pokemon Table, and in there it's going to bring in Pokemon Table and it's also going to have those Pokemon, but it currently doesn't have access to that data. So let's get it access to the data by bring in all of Redux and Redux Toolkit so you can get that data to that Pokemon table. Alright I’m going to do npm i to install Redux of course, the classic React Redux which binds it to React and then the Redux Toolkit. Redux Toolkit is just a very handy set of helper functions that makes it really easy to define stories and slices and actions and all the good stuff that we have in Redux. Now I'm going to rerun the server. Now I'll create a new directory for our Redux store and I'll call it store. Seems like a decent thing to use now. I think that we're going to have a search slice and that search slice is going to have the list of Pokemon plus the current search terms since we want to end up building a search page. So the search slice is going to be the slice of the store that manages search. So first off, we'll bring in Create Slice because we want to create a slice. They'll bring in payload action because that's important when we define the parts of the slice. And of course, we'll bring in our Pokemon, which is the data that we're going to be using. So what does our state look like? Well, our state is going to have our search string and it's going to have the startup Pokemon. That's the initial set of Pokemon that we got. The startup pokemon is just going to have our server Pokemon in that initial set that we get during the page load for the dynamic stuff that we use when we do a search, we're actually just going to use the data that is stored within the RTK or React Toolkit query, so we don't actually have to manage that. Now we'll create a slice. It's going to be a named search and then have its initial state and then we need to give it some reducers. These are the functions that you can call on the store in this case, set search to go and set a search value. The payload action for that one is just going be a string. You're just updating the string of the search and then an action to set the start up Pokemon. And this is what the page is going to use to set the Pokemon in the store. So it's just going to set that startup Pokemon to anything that's in the payload. And what do we export out of here? Well, we're going to export the set search and set start to Pokemon actions, which search Slice has created for us. That's one of the really great things about the Redux Toolkit is it gives you all of these really handy functions so we don't have to define our actions by defining them in the reducers here. It has already created actions for us and those actions are setSearch and setStartupPokemon. Awesome. And we need to have the reducer for this slice and that we export as the default. Now a slice is not a store, so we need to go create the store, which is going to be our global store, which is going to bring in any number of slices or APIs. So we'll create that on index.ts in here and we'll bring in ConfigureStore from Redux Toolkit. Again, a very nice easy way to create a Redux store. And we'll bring in our search slice that we just created. Then we'll use that configure store to create our store and we'll just give it our reducer, which we'll put on search. And it's the search reducer that we got as the default out of the search slice right here. Now, when we connect this to React Redux, React Redux is typed, but it doesn't know the schema for our store, so we need to give it the schema for our store and our dispatcher. So what we do for that is we export some types out of here. The root state is the the total state of the store. It is the output of store. I get state any time you call store to get state, you just get the whole store and we're going to get the return tab out of that. Pretty simple. And then the app dispatch is the type of the dispatch which is automatically created for us. You could do command-K command-I here and see that we have a search state, which is the output of search. All right, now we have our store. Let's go over to page, that's X and we'll set the data in the store. So we need to bring in our store first. So bring it in from @ store. And we're going to bring in the action creator of setStartupPokemon. So with that, we're going to just call store to dispatch and then we're going to call that action creators that startup Pokemon and just give it the data. Let's see if this works. And it does, but it's because we’re pointing at the wrong component. I'm looking at Pokemon Table here and I'm giving it the data, so I want to look at this SSR Pokemon Table instead. All right. Yeah. Now there's nothing coming through because as I say, our Pokemon table isn't actually connecting to the store yet. So let's connect it to the store. To do that I'm just going to bring in the store and then here for the store to get state, search and then startup Pokemon. Hit save and go and there you go. Now that's an example of using Redux during SSR time. Now, if you're building an e-commerce site and you want to go and get all of the information about the product and you want to have some images and you only want those to be rendered on a server using React Server components because they're not dynamic, then this is the way that you want to do that. Page is a React server component. It only runs on the server. We have no client side components yet, so the only way that we can get data from and to the store is to directly connect to the store directly call store, not dispatch, directly call store to get state. If we're to use the React Redux hooks, it would tell us that we can't do that because you can't use hooks in a React server component context. So that's why we're not using React Redux hooks here. Now, we do want to kind of lockdown this particular part of it because we've now looked at SSR, and if that's all you're doing, then I think having some example code that shows that only is good, someone go take this page and I'm going to go create directory here called Pure SSR inside of the app directory. And then inside of their page.tsx and I'll just paste off this in here and now if you just want to see how to do SSR, only then you can just look at this pure SSR directory and it's going to have that before we've gone and added any kind of client side craziness into it. So let's actually try this out. We can go over to our pure SSR route in Arc and there you go. And you can see that when you view the page source, we're getting all of the data in there. Ivysaur our Wartortle. So it's all being done on the server, but it still uses Redux. Really nice. Okay, let's go back over to our home page, make sure that we're on the home page, because now we're going to start using Redux both on the client and on the server. It's really cool. Okay, let me close a few files here so we don't get confused. They're going to turn our homepage into a search page, and to do that, I'm going to create a search input component. That's where we're going to type in our search. So I just bring in a very simple search field, and then I'm going to bring that search input into the page and replace this as our Pokémon table with that hit save. And there we go. We've got our search input. Woo! Now I want to go and connect our search input to our store. So I need to bring in a bunch of stuff for the store itself. I'm going to bring in, these should seem familiar, useDispatch and useSelector from React Redux, the type selector hook from Redux. I'm going to bring in our setSearch and we are going to create our app dispatch in our app selector. So let's check this out to see does it work? Okay, so this is telling us that we are bringing hooks into a React Server Component, but is this a React Server Component? I mean, we want it to be dynamic, right? Well, how do we tell it that? Well, we need to tell it that it's not a React Server Component, that it's a hybrid component or a client component, whichever way you want to say it, a component that runs both on the server and the client. And the way that we do that is we put in the use client directive and now we can use those hooks. Cool. And notice we didn't even have to actually call the hooks to get that error. We just brought in the hooks and that was bad enough. Okay, now we're going to get our dispatch in our search. The dispatch is what's going to allow us to set values on the store and then search is going to give us the value of the current search field. So we’ll just use value search. And then when we change, we're going to dispatch a set search action creator with the target value. So let's try it out. Let's see. Okay, Now we've got a different error. This error is telling us we don't have a provider. So if we go over here to useAppDispatch, well, on what store. useAppSelector okay. But on what store? You haven't actually told us what the store is. So to do that, we need to create a provider and give it the store. So I'm going to go over here into components and create a new file called Provider, and that's going to bring in our store as well as a React Redux provider. And then I'm going to give provider our store call, but not good because we haven't actually used provider yet. Okay. So now I need to bring our provider. I'm going to go bring that into our page and then wrap our search and put in that looks pretty good. But how do we know it's working? Let's go over here to our Visual Studio code, go back into our search input and then we just drop in here div with that search looks great. Cool. So we know that we're getting and setting from the store properly. All right. Now we also want to show a table right below the search that shows the Pokemon that we found and we are granted the default. No search pokemon already through start up Pokemon. So let's see if we can get to that. So I'm going to bring in our Pokemon table component and then get the start up pokemon and then just throw the startup Pokemon at our table. Okay, let's see how we go. All right, well we have a table, but we don't have any pokemon, so why is that? Well, we haven't moved the state of the pokemon from the server to the client. To do that we need a client sign component that takes the start up pokemon and then sets the store on initialization on the client. So how can we get data from the server to the client? Well, what's interesting to know is for client side components use client components. Anything that comes in as a property is serialized from the server to the client. So what we can do is in page, if we have a component here where we just gave it a property of the startup Pokemon, that data will go across from the server to the client and then that component could then preload the store with that Pokemon. So that's what I'm going to do. I'm going to go create a preloader component that will take in Pokemon and set up the store is going to be a client side component. It's going to bring in the store, it's going to bring the type of Pokemon and is going to bring in our setStartupPokemon because that's what we use over here to set it up on the client. So basically name and do exactly this, but on the client, let's define our component. It's going to take in Pokemon and it's then going to set the Pokémon. So now as a React component it has to return something. So is this going to return NULL. Now we only want to set this data once. So to do that I'm going to just use a reference to track it and we'll say how we loaded when I started False. And then if we get run, then loaded current is going to be true. And then I go take this code, the store dispatch, and I'm going to drop it in to this preloader. Okay, looks good. Let's go over here to our page, bring in our preload, our put this right up at the top here and give it the pokemon's with the data. Hit save. And let's see. Tada! Now we have it both on the server when we need it in pure SSR and we have it on the client by channeling it through the props of this preloader. So when it comes to preloading the store state for your client, this is a decent way to do that. Okay, now that we got this working, we actually want to go and make this search work, right? We wanna be able to say, Bulb make a request to our API off the client. So how do we do that? Well, we're going to use the really nice React toolkit query. If you have React toolkit, there's no reason not to use React Toolkit. Query is a fantastic resource. So do that. We're going to go create another slice, an API slice, and we’ll call this one the Pokemon API and into there I'm going to bring in Create API from Redux to get query as well as fetch base query. That's how we go and set up where the base of our all our queries are and of course the Pokemon type. Now we're going to call this reducer because this is creating an API reducer for us. We're going to call it the Pokemon API. So where are we getting our data? We're getting our data from localhost 3000 API. We're going to see that we have some tags. That tag is a type of Pokemon and then we're going to say that we have an endpoint and that endpoint is search and that call is search. So slash API slash search with the name and you give it Q for query that's defined here, that's the parameter type. And then the return type is an array of Pokemon. And we are going to return this tag Pokemon with the search results. Now we need to bring it over into the store, so I'm going to go and bring in our Pokemon API and I'm just going to add that as a reducer and I might save and see we get all right, it looks pretty good. And then I'll bring in some required middleware. This is all documented over on the RTK site. So let's hit our can see we go Cool. Awesome. Still works. Now if you are familiar with RTK query, you might be asking yourself why I'm not using the slash react bindings here because those actually make it super easy to use this. You can just basically use a bunch of hooks that it automatically creates to go and get the data. But if we save this and we hit Arc, what we see is this you get the create context issue. So what's happening is that the server is bringing in the store because it needs the search reducer. Now, the store also includes this Pokemon API reducer which it's not using but still is referenced and that Pokemon API reducer because we brought in React is creating hooks. And you saw earlier that any time you even think about hooks on the server, you get this create context issue. So we can't use the really nice API hooks without either creating another store, which would be a huge pain in the butt or well, in this case we're just going to do it manually. So okay, we're going to take that out. Then we're going to go back over to our search input and we're going to bring in our Search API because we want the Pokemon API out of there and we're going to construct our own data selector, which is going to get the result of the query out of the Pokemon API. So Pokemon API is going to cache a number of queries for us. Those are all going to be given specific names, for example, search and then the search string that we just used dot data and that's going to be a type Pokémon. So let's bring in our Pokemon type. And now down in the Pokemon table, we're going to say that if we have a search, which means that the length is greater than zero, then we're going to use the data that's come out of the Pokemon API, or if that doesn't exist yet, in that it's still in transit or in flight, we'll just use an empty array. But in the case where we don't have a search, what is used to start Pokémon because we already know what the empty case is. So we'll use that for our start up. And now let's try it this out. Okay, Looks good. And we're doing the right thing. So now if we have a search, we're showing the results of the search. But of course there are no results because we're not actually initiating a Pokemon API search. So how do we do that? Well, what we can do is we can say if we have a search, then we should initiate that API. And to do that we would look for an effect. We want an effect to happen when we change search, which means that we probably want to use useEffect. So we're going to use useEffect down here and we are going to depend on dispatch which really never changes and search because that's going to change every time we type a character. And then when we get a change to search, we're going to dispatch the Pokemon API and point search and we're going to initiate that search with the search parameter. Let's give it a try. All right, cool. That worked really well. So what do we learn? Well, we learn how to use Redux on the server. We've learned how to get server state to the client using a preloader. We've learned when to use stuff on the client and when not to when it comes to hooks. And we also learned how to use React toolkit query and avoid the issue of bringing in those react bindings, which would cause an issue with the server. But just go to the API endpoints directly. Well, I hope this helps you out with your struggles with the NextJS 13 app directory and it's integration with state managers, including of course Redux. If you have any comments or suggestions, I would love to hear those in the comments right down below. Of course. In the meantime, if you like this video, be sure to hit that like button. And if you really like the video, be sure to hit the subscribe button and click on that bell and be notified the next time a new Blue Collar Coder comes out.
Info
Channel: Jack Herrington
Views: 67,562
Rating: undefined out of 5
Keywords: next 13, nextjs 13, next.js 13, next js 13, nextjs react 18, mui nextjs, vite nextjs, nextjs, next js, next.js, nextjs react, nextjs typescript, next js typescript, nextjs npm, react 18, vite, react, reactjs, react.js, react app, nextjs redux, redux next js, redux next js 13, redux next js ssr, nextjs redux ssr, next js redux, next js 13 redux toolkit, next js redux ssr, nextjs 13 redux, nextjs 13 redux ssr, next js 13 ssr, nextjs ssr, react 18 ssr, react ssr
Id: dRLjoT4r-jc
Channel Id: undefined
Length: 27min 5sec (1625 seconds)
Published: Wed Mar 15 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.