Rethinking Server State with React Query

Rate this content
Bookmark

The distinction between server state and client state in our applications might be a new concept for some, but it is very important to understand when delivering a top-notch user experience. Server state comes with unique problems that often sneak into our applications surprise like:


- Sharing Data across apps

- Caching & Persistence

- Deduping Requests

- Background Updates

- Managing “Stale” Data

- Pagination & Incremental fetching

- Memory & Garbage Collection

- Optimistic Updates


Traditional “Global State” managers pretend these challenges don’t exist and this ultimately results in developers building their own on-the-fly attempts to mitigate them.


In this workshop, we will build an application that exposes these issues, allows us to understand them better, and finally turn them from challenges into features using a library designed for managing server-state called React Query.


By the end of the workshop, you will have a better understanding of server state, client state, syncing asynchronous data (mouthful, I know), and React Query.

96 min
15 Jul, 2021

Video Summary and Transcription

Welcome to my workshop on React Query, a powerful library for fetching data in React applications. We explore custom hooks and fetching data, as well as using React Query for data fetching and caching. We also learn about handling mutations, optimistic updates, and query prefetching. React Query simplifies global state management and provides a seamless user experience with instant data access and background fetching. The workshop concludes with a discussion on comparing React Query to SWR and the importance of sharing positive experiences with the library.

1. Introduction to React Query and Workshop Overview

Short description:

Welcome to my workshop on React query. React query is a powerful library for fetching data in React applications. It addresses the challenges of managing asynchronous server state and provides features like caching and pagination. Today, we'll be working on a simple app to enhance the user experience. I'll walk you through the code and explain how React query works. Let's dive in!

Welcome everyone to my little workshop on React query, this is gonna be super fun, there is a lot to cover today and I really wish I could see everyone, but I know that's not possible, but just, hello, I'm super happy to meet all of you guys. I hope that you guys have a good time today.

So, just a quick intro to what we're gonna be doing, hopefully you joined this workshop because you've heard about React query and you're excited about what it would be able to do for you in your applications, so React query actually just turned one year old yesterday, which is really cool. In a year it's had 13,000 stars and almost two million downloads on NPM and 188 contributors so it is awesome how far it's come. React Query is kind of becoming one of the de facto libraries for just fetching data in your React applications. And that's kind of at a high-level perspective of what React Query can actually do for you.

The reason that React Query exists is primarily because of this big journey that I took over the last three years discovering that server state and client state are not the same. And when I talk about server state and client state, really what I'm talking about is state that lives on your server, like in your API or somewhere that is not on the client. And then when I talk about client state, I'm just referring to things that live only in the app instance in the browser that the user is interacting with. So that could be things like the state that's in the router or if a sidebar is open, what page they're looking at. All of those things that are not persisted to the server, that's all client state. And when I found out that these things are not only just different in how they're stored and what not, but they have completely different paradigms around managing these types of state. When I found that out, I came to the realization that all of the global state tools that we have like Redux or MobX or even React Context for that matter, they're just not quite there. They're not great tools for managing asynchronous server state. And the reason for that is because everything is different with asynchronous state. There's things that you don't even think about like caching and synchronization and how often you're synchronizing and how do you tell your app when to synchronize or how long to cache. Then there's things like pagination and infinite scrolling. And there's a lot that comes with server state that you don't even think about with your client state. So instead of trying to teach everybody the differences and, you know, try and let everybody know about this, these unique challenges, I decided I was going to build a tool that would help people utilize those challenges as features and kind of teach you in reverse. So you learn the tool, you use the tool, and it helps you learn the underlying concepts kind of like Redux, but just for server state, I guess. So that's what we're going to be doing today.

I have built this application. I'm going to send out the link one more time just so everybody can get it. This, let's see. So this repo is called React Summit 2020 React Query. And what it is, is a really simple app. I'm going to run you through what the app does right now just so you can get a feel for what it actually does. There's a home screen. It says welcome. Pretty sweet. And then there is a blog page that shows us our posts, our blog posts. And it only shows us the first little bit of each blog post, the content. And we can click on one of these blog posts and see the full content for the blog posts. We can do that for any of these. So we can come over here and click on this one. We can also go to the admin screen and we can click on those same blog posts if we want to edit them. So if we want to go to this test blog post, we can change this to say something completely different. Yeah, we changed it. That's great. We can also go and view the post again. We can delete blog posts with that delete button that was in there. Let's see, it's in the admin. So if we go to the admin, we can go to our little test post here and hit delete, and that will delete our test post. And we can create new ones too. So we'll add back our test create post. Really simple, not a whole lot to it. But we're going to be taking this really tiny little slice of an application and just making it amazing. Like we're going to make the user experience for this tiny little application the best that we can. Before we can do that. I'm going to take you through some of the code. And I really wish we had enough time to actually go through and build all of this from scratch. I did a demo test the other day and, you know, building all of this from scratch and the entire workshop took about, you know, three hours. So I had to kind of skip some of this stuff here and prebuild some of this. But luckily, you know, the source code is available so you guys can go check it out, exactly how it's built. So the way that the repo is on master right now is exactly how you're seeing it in my code. OK, we'll just dive straight into it. There's some really cool stuff that I'm doing with Next JS. So if you're curious, I'm using Next JS instead of Create, React App, but I'm building it just like I would a normal Create, React application. So if you're curious, you guys can go and read this little fun blog post that I did on Git or I guess it's a gist on how to use Next JS instead of Create, React App. But that's what we're doing here. This is our index.js file, and this index.js file is other than kind of this, these little Next JS hack things here is going to look pretty, pretty basic. There's a browser router that we're getting from React Router. Don't need to know too much about that. There's a wrapper here that's just giving us some base styles. There is a sidebar, yay, sidebar that renders this stuff over here. And then we have our main content that is where we're doing our routing. The home screen you can see renders welcome. Sweet. The admin renders the admin component and so on and so forth for each one of these paths. So the admin blog posts post ID route will render the admin post.

2. Exploring Custom Hooks and Fetching Data

Short description:

In this part, we explore the use of custom hooks in React query. We start with the usePosts hook, which returns a post query object containing isLoading, isError, and data. We then dive into the individual blog posts, where we fetch the post using the usePost hook. The loading state is displayed, and we render the title and full body of the post. Finally, we examine the usePosts hook, which fetches data using Axios and sets the loading and error states accordingly. The resulting state is wrapped in an object and returned, allowing us to determine what to display based on the loading and error states.

Blog blog post, just like that, nothing to it. And then if we dive into blog here, let's just go to the blog component. This is the home screen for blog. All we're doing is we are using this custom hook called Use posts to get a post query. And you'll notice something interesting here if you've already used react query. You might be familiar with this syntax right here of is loading is error and data. So we're not using react query yet but I'm still showing you how you can model your custom hooks to be reusable and upgradable really easily.

So we have this use posts. Custom hook right here. And it's just giving us back this post query object which really just has is loading or is error. Then it has this dot data right here. And data is our list of blog posts that we can loop through and, you know, render the title and the body and a link to the blog post. It's pretty cool. So that's all that's happening here. Let's dive into this use posts. Well, I wonder what would be better first. Let's go into the individual blog posts first. So we'll click on one of these individual posts and we will dive into this posts component. This is under screens slash blog slash posts. Again, really simple. We're grabbing the post I.D. from react router so we know which post we're rendering. And that's just this little number one up here in the U.R.L.. From there, we are using that post I.D. to go and fetch the post from our server. So we're just sending that into this use post hook and it's giving us back a post query. And then we're doing the same thing. We're just checking, is it loading? Is there an error? Otherwise, we're just going to render the title in the full body. You can see that loading state right there. If we reload the page, you get that little hard loading state there. OK. So let's finally let's dive into what some of these custom hooks are doing. Let's go back to the blog page itself and let's dive into this use posts hook. So. This use post hook is very naive, but it's probably going to look pretty familiar to what you're used to seeing when it comes to fetching data and react. Right. This is how everybody starts out. You've got some state. This could be even just react. You state. I just use reducer because it's kind of fun sometimes, but it's the same exact thing. We start off with is loading true. We create a little fetch function here. And as soon as we start fetching, we make sure that it says is loading true. We go and we get our data using whatever we want. In this case, we're using Axios to go grab the posts from our API that we have. And then as long as that comes back successfully, we're going to set its success, true and data. Otherwise, we're going to set is error true and error so we can see the error. And then we're going to run that function when the component mounts right here. So use effect is just saying, see, when it whenever this component Mounts, we're going to run this function once fetch. And that's why that's how it kicks off this loading sequence right there at the very beginning. This fetch is running once. Since we don't have any dependencies, it's never re fetching automatically. So, you know, if we if we come back to the screen, it's just going to kind of stay this way forever. Any time that we unmount it and remount it, like if we go to a home and come back to blog, it does that fetch again every single time this component remounts. And then we just take kind of all that state and we wrap it up into that object so that we can reference, you know, is loading is success and is error and data and error. And we even expose the fetch function right here, all on this object coming back from use posts. And that's what we're getting back here. This posts query object. And from there, we can kind of determine what we should be displaying based on what's happening, loading, loading, error, show the error message. And we can just assume that. Let's see. Ivan said, How come you're not setting it to is loading to false when the data comes back? Well, I kind of am. This is just replacing the state. It's not it's not adding on to the existing state. So basically, when I say set state, it's just replacing everything. So it can really only be in one of these states at any given moment. Loading successor error. So, yeah, that's what this little reducer is doing instead of set state. So good question.

3. Exploring Custom Hooks and Fetching Data (Part 2)

Short description:

React hooks make it natural to fetch data. The usePosts custom hook fetches individual blog posts. The usePosts hook fetches data using Axios and sets loading and error states. The admin screen uses the same usePosts query. A create new post form allows users to submit new posts. The useCreatePost hook simplifies post creation logic. It returns a function to create a post and state information about the mutation.

And that's why that's how we can realistically and or not realistically but pragmatically determine which state we need to be displaying. So really basic, I'm sure we if you've used React at all, you've probably written something like this before. And that it's good because this is natural. This is what I believe, people go to this, you know they make a custom hooker you can even do this inside of your component itself, because the custom hooks and react hooks make it really natural to fetch data.

Let's go into our individual blog post now and look at the used post custom hook that we have here. So use posts, all these hooks are just inside of this little hooks directory it's kind of small on my screen. Maybe if I zoom in, you can see that I don't know if you saw that zoom, but it's very similar, all we're doing is again we have a reducer. We are kind of writing the same code again over and over. We have to add a use callback around this one because the post ID is changing, sometimes. And then we just call the fetch again and it goes and fetches the individual blog post. Puts it onto state.data. And it's all kind of the same thing we're just, you know, doesn't matter how we fetch the blog post. This is usually how we write data fetching with hooks. So yeah, there's nothing, there shouldn't be anything too surprising there if you've used React hooks to fetch data this way. So, some of it can change a little bit when you decide on how you're going to template it. Maybe some of you like to destructure and say like, is loading, is error, whatever. And that's totally fine too. I just call this the post query. Let's go to the admin now really quick. Let's see, if we go to admin slash index, this is our admin screen right here. And it's very similar to the blog page where we are using that same use posts query to just go grab all the posts. We will come back to this in a second. We take that post query and we're doing the same exact thing. We're saying hey if it's loading then show loading and in fact I'm not even showing an error state here. I guess I should. Post query is error. If there's an error we're going to you know show the error. Post query dot error dot message. Otherwise we're just going to assume that everything is okay and we can render our posts. I have to bring this stuff back. It looks exactly the same as the homepage just a little bit different styles. It's still just a link to the individual post in react router. Down here underneath it we have this create new post form. We're not going to get into the post form that much. It's just a really basic form that just has 2 fields. Title, body and a button. Couple of handlers so that we can say what does the submit text say? Does it clear when you submit it? What are the initial values? Kind of an on submit handler so we can grab the stuff when it gets submitted. When you click on submit right here if we go up here we have this on submit callback. It's giving us the new values. We're actually using this create post function to go create a new post. After that post gets created we're calling the post query.fetch part right here. Saying we know that a post was created. We need to refresh our page. When we add tests to and whatever we hit create post. It's like it was creating it refreshes, it takes the whole post query into that loading state again which is kind of annoying. I'd like to keep seeing that list. We're starting to get into some of the annoyances of just fetching with just react hooks. Let's take a look at this custom hook. This might be a pattern that you're not familiar with. Honestly is creating custom hooks for these types of actions. Honestly I create custom hooks for everything that I do because it makes it so easy to compose everything together. Instead of putting all of my logic on how to create a post inside of this function right here, this component I put it inside of this use create post hook. And what it does is it returns an array with the first slot is create post, like the function that we actually call to create a post. And the second slot is like some of the state and information about, that is about that mutation. We're getting dry. Let's dive into that. This is the use create post custom hook. And it's really simple. We're just going to have some state tracking here, and instead of firing off a request, we're just creating a function that we can call that's going to take the values that we give it and go post them to a server, and keep track of is loading, is success, and is error. So when we call this mutate function right here, this create post, create post info is going to change and give us all the state. And we just show it in that button. So if you look at that button right here at the bottom of the screen when we do test three, you'll see, I click in, it says, saving... It does saving... I guess it didn't do it. I wonder if it's too fast. Our API might be really fast, which is totally fine. I'm not worried about it. But that's what it's doing. It's tracking all the state there so we can tell what's the status of our mutation. Also, guys, don't be shy.

4. Exploring useCreatePost and Post Component

Short description:

The useCreatePost hook returns a function to create a new post. The post component in the admin shows the post form with initial values. The usePost hook fetches a single post using the post ID from the URL. The useSavePost and useDeletePost hooks handle saving and deleting posts. React Query works without using global state, relying on custom hooks and component state. Pre-fetching and suspense are options for reducing loading screens, but both require some form of caching. Building a caching system using global state is not necessary with React Query. React Context can be used to share data across the app, but it still requires manual caching. React Query can be used with suspense, but suspense is still experimental.

You can ask as many questions as you want. But if you think it's a question you'd rather ask until the end or it might get answered, maybe you can hold onto it for a bit. But if you're curious about something, feel free to ask.

So that's the useCreatePost, really simple. All it does is return a function that we can call to create a new post. And then the only other piece to all of this is the actual post component in the admin. So right now we're in this index file. If we go to slash ScreensAdminPost, we get into the actual post edit screen. So let's go into this test3. So this one's a little different. What we're doing is we're showing the post form that we used on the other screen, but we're just giving it some initial values. It's that same form that we used to create a post, but it's just filled in already for us. So it has the title and the body of the post. It has save posts, and then we added another button down here underneath it that lets us delete it. And we're doing the same thing that we're doing to look at an individual post. We're just grabbing the post ID from the URL. We are passing that to use post, so the individual use post hook, which is the same hook that we use to go get a single post like on the blog part. And then, let's see, let's go back here. Oh, I think I lost my spot. Post screens admin, sweet. And then we have two other functions that we can call here. So we have use create post that was on the other screen. This one has use save post and use delete post. And if we go and look at those, they're like exactly the same as use create post. We're just doing a little different API endpoint there. Same thing with use delete post. We're just doing a delete call to this endpoint. Really, it doesn't matter what you're doing for your API. It could be Graph QL or Firebase or whatever. Doesn't really matter. Just the concepts here are that it's returning that mutation function that we can call to save a post with new values or delete a post using the post ID. It also gives us that state information.

This works, honestly. It's interesting. We're not using anything special here. All we're using is React. Does this application work? Absolutely. It definitely works. We're not using global state at all, if you notice. All we're using is custom hooks and hook state, like component state, right? So yeah, it definitely works. If you were able to bring it up on your computer and build it and play with it, it definitely works. You're probably not surprised. Some apps definitely do feel like this. Where, everything you click or do is kind of like this loading screen. It's like, oh, let me load that for you. For the most part, we have gotten used to seeing this in React. And when it comes to asking, can we replace that? How do we get rid of that loading screen? There's really two options that are well-known out there. And one of them is pre-fetching. If somehow you can pre-fetch that data so that it's immediately available, then you wouldn't see that loading screen. But to do that, you would need to have all of your server cache, all of this server data, in global state. Because you need somewhere to store it, before you look at it. The other thing too is suspense. There's a lot of buzz around when suspense for data fetching coming out, and concurrent mode and stuff. And yes, it will solve some of that, to some extent. But it's still going to require some sort of a cache to operate. So, it seems like any direction you go, at least in the well known React ecosystem today, everything is pushing you towards global state. And this is usually where everyone's like, oh, well I need a global state manager then. Because I want to be able to share all of this data across my entire app, And every single time I hit the blog page, I don't want to have to re-fetch that data every single time. And, totally viable. You should not have to re-fetch that data every time. But the part where everybody seems to go wrong is thinking that they need to build this caching system on their own. And that was the way that it had to be for a long time. If you were using Redux to manage all of this state, that's exactly what you're doing. You are managing a very complex caching system on your own using Redux. That's really what you're doing. And, if you're doing it with React State, we could take all of this data, and I've done it before in some other really long workshops, and we've actually re-written all of this to use React Context. So the posts are shared through the entire app and we interact with the context through a bunch of hooks. And, yeah, it definitely works, but all you're doing is just solving the caching problem on your own and probably not taking advantage of all the other things you could be doing with the caching system. But even better. Rain Hall asks, Can React Query be used with suspense? Well, suspense is experimental right now.

5. Using React Query for Data Fetching

Short description:

React Query allows us to keep the same custom hooks feel and not worry about managing global state. We create a new query cache for our application and provide it to the rest of our app using the React Query Cache Provider. In the blog index page, we convert the usePost hook to use React Query's useQuery hook. We define a key for the query and use the fetchPost function to fetch the data. This simplifies our data fetching code.

And so, in an experimental way, yes, you can. We have written React Query to be suspense compatible as much as we can for now. So, obviously, it needs a lot of polish still because Suspense Mode is kind of up in the air still on how everything is going to work. But for now, you can use it with suspense and it works pretty well.

So, where do we go from here? We have this app, it's working, and it's got a lot of loading things. It doesn't cache anything. It feels really slow. We're probably fetching, you know, we're waiting on the server every single time that we want to look at something. It's really annoying. I just want to take this whole experience and make it as good as possible.

Instead of going the route of, hey, let's add Redux or let's add MobX or let's do React Context and let's blow this global state thing out of the water. I want to ask you truly, why is it that we couldn't just keep going with this pattern that we have? We have usePost and it's just a custom hook and usePosts, where we're fetching this data here and all these mutations where we're just saving and deleting and creating. If you look at this code, it's really not that complex, it's just the way that it's managed doesn't scale and doesn't allow us to do the same things as global state.

Well, what React Query is going to allow us to do is keep kind of this same custom hooks feel and just write our code and not have to worry about managing any global state at all. And there's really nothing else to say other than lets just dig in and, you know, let's get to it. So React Query, you know, it's necessary that we understand some of these pitfalls and how things work before I introduce React Query so you can kind of feel the pain, you know? It's always good to feel the pain for something when you're learning it so you can understand why that it's working the way that it is.

So the first thing I'm going to do is I'm just going to import I'm going to import a query cache class from React Query. And just to make sure that everything is okay whoops we're going to console log out query cache. Sweet! So query cache is something that we use to create a new instance of the query cache and I'm sorry but I'm going to have to disconnect one of my monitors because, wow, my computer is just wigging. Let's make sure that this works. I'm scared to do this during a Zoom. So let's see you go over there. You go over there. You go over there. Okay. Looks like it worked. Okay, I think my computer should be able to handle that a little bit better now. Okay, so we're logging out the query cache. What we need to do is create a new query cache for our application. So we're just going to say new query cache, and this query cache is what we're going to use to cache all of our queries, all of our data. Pretty simple, right? We have to provide that to the rest of our application. So underneath this safe hydrate right here, I'm going to use the react query cache provider, and I'm going to pass our query cache to it. And we're going to move this down here. So the react query cache provider comes from react query as well. And that's just letting us supply our query cache to anything inside of our app that needs it, any use queries that we use. So from there, I don't think there's anything else that we need to do here for now. We're going to be bouncing around a little bit. Let's go into our blog index page.

So we have this blog index page, and the reason I wrote this stuff like this is because if you have, you know, this is loading, is error, this kind of style of templating, and you move to react query, you really don't have to change a whole lot. Like, I love everything about this blog page. Let's just go to the use post hook, and let's see if we can just convert this to use react query. So, I'm going to import use query. Use query is one of the main hooks from react query that just lets us create kind of a subscription to some data, just arbitrary data. Above all this we're going to do return use query, and we need to give this query a key. I think I'm just going to use posts right now. Keys can be just a simple string or an array. It doesn't really matter right here. Just to keep consistent, we'll use an array. So, that's the unique identifier for this query. And then we're going to use the function to go and get the posts. So I'm just going to grab this axios.get call right here that's going and getting that data. And I'm actually going to const fetch post. And we're just going to make a function that will just fetch some posts right there. And all of this gets to go away. Ha, ha, ha. Well, let's see. Geospatialmax says, Is there any disadvantage using global query cache as opposed to writing one via React query cache provider? Well, in future versions of React Query, the global query cache is no longer going to be available. It's still supported today. So you can still use that, but the way that I'm showing you today is going to help you prepare for the future. Flora says, Does the key always need to be in an array? It doesn't. It can only be a single string or an array of anything. Really under the hood, if we didn't supply an array, this string of posts would just get converted to an array with posts in it, under the hood, if that makes sense. So it really doesn't matter. I would just go with an array to be consistent. Yeah, geospatialmax, that's correct. It will be required in v3, but I'm showing you v2 today. And so, sweet. We don't have to import React anymore. And this is all of our data fetching code. This is fantastic.

6. Using React Query for Data Fetching (Part 2)

Short description:

useQuery returns an object with isLoading, isError, and data. React Query gives you back an object with all these things on it. The blog page and admin page both use the same usePosts hook. The individual blog page also uses React Query. The usePost component is modified to use React Query. The query key includes the post ID. Partial matching of query keys allows for selectively refetching queries. Inline functions can be used as long as they return a promise.

And also, since none, since useQuery returns, useQuery returns all of these, let's see, the query results. Let me show you the types for this. If I can find them. Query results. Eh, I'll just tell you. So it returns an object with things on it, like isLoading, and isError, and data, and error. The same stuff that we were using in our custom hook we had built before.

So, in what situation do we need to provide an array of multiple string keys, Ivan asks? Well Ivan, I'm going to show you in about five minutes. So don't worry. So, this is it. So if we go back to our blog page, this, all of this same templating Like, the isLoading, isError, and error and data. Yeah, I know it's convenient that I wrote it that way in the beginning, but it's true. Like, this is, this is how React Query works. It gives you back an object with all these things on it. So, if we save that, and reload our blog page, hopefully everything just works. Sweet. I'm going to close the console for a minute.

So, we go to the homepage, and then navigate back to the blog page. Check this out. Oh my goodness. There is no loading indicator at all. Awesome. That is, that's great. So we already have like a way better user experience. So, no loading on blog. That's great. Look what happens when we go to admin. What? So, the admin didn't need to load either because look, if you go to the admin. If you go to, oh come on, admin slash index. If we go to the admin slash index, it's using that same use posts hook, giving us a post query. And since we didn't need to change like is loading or is error, it all just worked. It's amazing. Let's look at what else we could change. Nah, we're going to do the admin in a minute. Let's just keep focusing on this blog page. Okay, so there's nothing else to do here. Like look how easy this is. You're just saying give me the posts and I'm going to show the posts. Yeah, sure, you get a loading indicator on the very first load, that's fine. We'll talk about that in a minute. Let's go into the individual blog page, so blog slash post. Now this one, same thing. We don't necessarily want to change anything that we're doing in this in this templating here, because it's all just going to work. What we really need to do now is just take our use post component and also turn it into to using React query. So let's go into our use post component, and I don't know if you knew, if you saw before, but really the only thing that we kept from all of this was like the way that we fetched the data. So let's just grab that and just delete all of this, because we're not going to need any of it anymore. Yep, posting a post is now broken. Miriam says, and that's fine, some of our app is broken right now, because we're in a in a broken state for like some of the admin, so don't worry about it, we'll get to it. Yes, you can use it with SSR as well, but we're not going to be going over SSR. There's docs in React query that talk about SSR. I also talk about SSR in my React query essentials course too. But right here we're going to be doing basically the same thing we did before, return used query, which we should get from React query. Yay, we don't need react anymore. Okay, and then to Ivan's question, when would we use multiple items in a key? Well, I'm going to key this as posts, but then I'm also going to add the post ID inside of this query key right here. Now I'm naming them the same thing, I'm giving them the same prefix, so the one for our regular posts looks like this, right? Now I want to tell you guys that this is a smart way to architect your keys, because there's going to be something that we can do that lets us forcefully refetch some of the queries on our page, and you can partially match query keys. So you could do something like this, like invalidate posts, right? And it would partially match both this query and this query, so that you don't have to go through and invalidate every single post ID. That's ridiculous. So that's why we're going to use the same prefix, but then give it a unique post ID. And then we just need to give it the function to go fetch it, and this is cool too. You can use inline functions here as long as they return a promise. So we're just gonna do fetch posts with the post ID that we're getting back from here. And that should be it. So let's reload our page. Just start fresh. Hard loading. Okay, that's great. If we go home, back to blog, it doesn't load anymore. That's great. If we click on a single blog post. Okay, so we see the hard loading for a second, and then we have, we get the actual data.

7. Caching Data for Instant Access

Short description:

React Query caches the data, making it immediately available when revisiting a post. This eliminates the need for loading indicators when navigating between posts. It enhances the user experience by leveraging user interactions and providing instant access to cached data.

So back to our post here, we're seeing the data. Now check this out. If we go back to the blog and then click on that post again, it is there. It's cached. It's immediate. It's great. If we go to another post, yeah we'll get a hard loading state, but then we can go and visit those posts as fast as we want and it'll just always be there. It's amazing. I love it. So it's kind of like building on top of your user interactions. If somebody's visited something already, they're probably going to get, they're not going to get any loading, loading indicators anymore anyway. Right.

8. Exploring Data Reloading and Background Fetching

Short description:

Let's explore how React Query handles data reloading and fetching in the background. We can use the isFetching property from React Query to determine if data is being fetched. By adding a loading indicator, we can see when data is being fetched in the background. React Query caches data, providing a hot reload-like experience. The data is cached in the query cache singleton, not in React context. To skip the cache, you can use the useQuery hook with the refetchOnMount option set to false. React Query also provides the useIsFetching hook to check if any data is being fetched in the background. For more in-depth information, refer to the documentation or consider taking a React Query Essentials course.

Now this is interesting. You might ask like, well, when is the data actually reloading? Let's go back to the blog page here and I'm just going to add in something to our blog that's going to give us a little bit of a hint as to what's happening. I'm going to use our post query, but I'm going to use something from react query called is fetching.

Oh, Mikhail, Mikhail FKosta said, could you write a middleware to change the shape return by use query, for example, instead of returning is there and is loading, you can, because it's just a custom hook, so you can call Use Query and get the result from Use Query and transform it however you want and then return it. So you don't even need a middleware React hooks, React custom hooks are your middleware. Pretty cool.

What about merging cache state with new data? ie someone else posted. Um, that's a great question. We're going to talk about that as well later on. We're getting there. I'm trying to. We'll get there. I'm just I don't want to jump the gun really quick so we can use post query is fetching to tell if we're fetching in the background. So if we are fetching, I'm just going to show a loader here. And loader comes from some little styles that I built, which is fine. Wait. Yeah. So check this out. This little loading symbol tells us when it's re fetching in the background. So if we go to home and come back, you'll see that it's fetching in the background. But we're still seeing it so when that gets done it's updating with the new data. And watch this if we go into our store. Let's go into our store really quick. And for this main format this for this main body right here, let's just delete this and just say, boo. And save it. Watch what happens when I refocus the window. It automatically fetched in the background when I refocused on the browser window and it fetched it in the background here. Now, we can change it back. And we'll go into here and it's already updated. So let's go back to boo. Again, and we'll just do it on the individual post. We didn't show the little loading symbol like we normally do on this part right here yet. But again, it refetched in the background and we didn't have to do anything about it. So that's awesome. Everything it's like a hot reload, but for your data. So we could show a loading indicator here as well. We could go into the individual post page for the blog. Sorry guys, my computer is lagging just a little bit. So we can go into the individual post page. I could add that same background loading indicator on here. But really, I wanted to show you guys something cool. You can do that with all of your stuff on your page. So if we go back up to Index right here – whoops, Index.source – I have built this little component right here that I'm going to do after wrapper. It's called the Global Loading – let's see. Global – what is it? Global Loading Indicator? Maybe it's called that. I'm going to have to go check, but it is inside my components. Global Loader is what it's called. Let's just import that really quick from Global Loader, and we'll go take a look at what Global Loader does. All it does is it just has a little loader symbol right here, so it's just like spinning all the time. It's up here in the right-hand corner. You can see I put it up in the top right-hand corner. Right now it's just always there. But ReactQuery ships with this cool hook called useisfetching. We can import that from ReactQuery and say is fetching. And this is global. For all of ReactQuery, is something fetching in the background? I'm just going to take that, and I'm going to say if it's fetching, then we're going to have opacity be 1, and otherwise, we're going to have it be 0. Watch this as I click on the page again. Anytime that we're loading in the background, anywhere in our application, it shows us that global loading indicator.

Parojo says how does it cache the data? Is that a React context? How could I skip the cache if necessary? How can I also pull some every x seconds? Could you explain one more time how this differs from global state you would get using the general approach? This is so awesome. Lots of questions. I can't dive deep into these questions because we don't have time. I would just suggest that you read the documentation because it talks about all of this. And also, if you're really, really curious, you can take my React Query Essentials course. I talk about this. But it is not cached in React context. It is cached in that query cache singleton that I showed you in index.js. So right here, it's all in that little – it's in that instance right there. We're just providing it to our React application through context. You can pull every X seconds. I'm not going to show you how to do that today.

9. Exploring Background Fetching and Data Refresh

Short description:

React query handles global state management automatically without the need for reducers or global action handlers. The default behavior is to always background refetch, but you can customize this using stale time indications. By setting a stale time, you can control how often data is refetched. React query also provides cache time options to control how long data is cached. In the admin section, we want all post queries to refetch when a new post is created, so we'll implement a way to handle this for the entire application.

But it's just like a really simple re-fetch interval option that you can pass to use query. How is this different from global state that you would get using the general approach? Well, it's different because you don't have to manage global state. I don't know if you noticed, but there's no reducers. There's no global action handlers. You're not managing global state at all. React query is doing all of this for us automatically. That's the biggest difference. There's a lot of other difference.

The course I mentioned is my official React query essentials course. Yes, you're getting links to it from a bunch of people now. I'll post it one more time at the end so you guys can have it. But let's keep pushing forward. We've got a lot here. We have great interactions now. I'm going to go back to our index page for our blog. I do this a lot in my applications where I show the background loader in some specific places. But honestly I kind of like the global loader for a lot of things, so I'm just going to take out that inline background loader. Now I'm just going to use this one up here in the top right-hand corner. That's pretty nice. Fades in and out, nice and pretty. So now when you visit places around the site, you can see that it's background fetching.

Now I'm going to read people's minds here. You're going to be like, well, it's background fetching a lot. Yes it is and that's because that's the default in React query, it's just to always background refetch. But you can tune your queries so that if you know that some piece of data doesn't need to be updated as often, you can use stale time indications to tell data how stale you will allow it to get before it has to refetch. In fact, I can show you that right now. Let's go into use posts right here. We're going to add an option here and call stale time. Five seconds. Okay. Now watch this, post is going to refetch. And you'll notice that I'm moving between these pages and it's not refetching in the background. Oh, but there it went. Because five seconds was up. So after five seconds, the data became stale and it refetches in the background. We could do it down to, like, two seconds or two seconds like this. And you'll notice that it won't refetch on every single one. But after two seconds, it'll become stale. So you get the idea. There's also something called cache time, which you can learn a lot more about in my course. I can show you right here, what if we can make it so that data only lives for five seconds overall? So if we come into the blog here. So here we go, it's five seconds if we come back, it's still there. But if we leave two, three, four, five, for five – for over five seconds and come back. Uh oh, that data was gone and we hit a hard loading indicator. So it's kind of like, hey, how long do I want to cache this data? Pretty cool. Again, all things you can learn more about in the docs and through my course. But I just want to show you guys that. It's pretty neat. So let's dive into the admin. So the blog experience is already amazing now, like a lot better. If we've already visited a blog post, it's just there. And we're going to make that even better. But for now, let's go over to the admin really quick and just check out what we can do there. So admin index. We're benefiting from the same stuff we're getting from use posts here. So we don't really need to do anything special there. When we hit create post, we're doing this post query fetch. Really, what it would be is refetch. But even that just seems a little manual, you know. Really, what we're trying to communicate here is that when a new post is created, we want to make all of the post queries in our app refetch, you know, and just kind of update. And this is only happening in this one component, which is kind of fragile. So Ravi asked what the default stale time is. Zero, data is considered stale immediately. Yes, it's aggressive, but if you read the docs, I like to use the term, aggressive but sane defaults. So yes, it works well with TypeScript. It's actually written in TypeScript, so it's pretty cool. What we're going to do here, instead of calling this refetch automatically, we're going to come up with a way where it just happens kind of for the entire application. So we're going to get rid of that for now. So if we come and test this out and do a new post, and we hit Create Post... nothing happened on our screen because we're not refetching, but if I were to focus off the screen and then click back onto the browser...

10. Exploring useMutation and Handling Mutations

Short description:

In this part, we learn about the useMutation hook in React Query, which is used for handling mutations. By using useMutation, we can simplify the code for adding new posts and deleting posts. The useMutation hook returns a mutate function and mutation info, similar to our custom hooks. We can directly return the mutate function and remove unnecessary destructuring. The useMutation hook can be used in conjunction with other options, such as onSuccess, to perform additional actions after a successful mutation. We can also use the query cache to invalidate and refetch queries when a mutation succeeds. This ensures that our data is always up to date. By using useMutation, we can streamline our code and improve the efficiency of our application.

Oh, there's the test4. It automatically refetched. Obviously, we're going to want to do something to keep this kind of refetch functionality in here, but I'm going to show you how we're going to do that with the next awesome hook from React query, that is for mutations, not for queries. So if we come into this useCreatePost hook, if we're talking in general terms, we've got queries and mutations. Queries are for synchronizing data. Mutations are for changing something. And this is what we're doing. We're changing the server. We're adding a new post.

Now, as you probably guessed, we can take all of this other stuff other than this POST part where we're actually posting the new value, we can just get rid of all this because React query has a useMutation hook that's going to just handle it all for us. Yes. So here's our useMutation. What do we do? We pass the function that's going to get called when we call this mutation. So what really happens is it returns a mutate and a mutation info, just like our other custom hook did, the function we can call to fire this off and the information about it. And we get the values right here. Whatever we call the mutate function with is going to give the values right here. And we just want to – we can just return this directly. You just have to return a promise. That's all you have to do. So we can get rid of that. And really what we're just going to be doing is matching what we had written before with the mutate and mutation info. So we can just get rid of that unnecessary destructure and just return use mutation directly. And that's it. Again, woo, get rid of React. And if we go back to our post page where we're creating the new post, so we say use create post, it comes in here and it returns use mutation, which gives us this object right here, this array. Create post is a function we call to create it. And create post info is the information about mutation that's happening. And then we can just call create post. So let's just make sure it works for now. Let's do test five. We'll add this. Watch the create post button. It should show our loading state. Yeah, saving, saved. Sweet. We can see it still didn't automatically refresh our page to show the new post. So let's go in and create posts really quick. And we're going to use one of React Queries or the use mutations options. We're going to use on success. And this is a function that gets called when things were successful. We want the query cache here because we're going to do something unique with it. To use the query cache, you know, in older versions of React Query, you could just grab it off the top here. But really what we want to do is use the query cache that we created for our entire app. Is this index.js right here? We want this. So let's export this query cache that we're using. And then we're going to import it in here. And we're going to import it from, let's see, I think that should work. So this query cache is an instance of our query cache. And it has a bunch of cool methods on it that we can use, so many of them, we don't have time to talk about all of them, but one of them is called invalidate queries. And invalidate queries takes a query key or a query key root that you want to fetch, that you want to invalidate and refetch. So in this case, you know, over in our used posts hook, the query key we're using is posts right here. And I want to refetch all posts. In fact, even if it was an individual post, I could just refetch that as well. But we'll just use the entire posts key right here. So when this mutation succeeds, we're going to invalidate the post. Let's go and do test six and hit enter. Cool, so you saw the background loading up in the top there and it automatically refetch. So now anywhere that we create a new post, it's just going to go and invalidate our posts and make sure that we're up to date. That's really all we have to do. So that that weird refetch thing here that we're doing, we don't even need that at all. In fact, this Create Post. Honestly, it could just be that because we can get rid of OnSubmit, too. We can just call it Create Post. Boom, getting rid of some code. So yeah, that's mutations. Let's do that, but for all the mutations really quick. So let's go to delete post. It's going to be basically the same exact thing, but we're going to take this Delete call instead. So instead of deleting, we're going to, or instead of saving, we're going to delete and we get the post ID right here instead of values. We'll import UseMutation.

11. Updating and Invalidating Queries

Short description:

Get rid of React. Import our Query Cache from our index. When deleting or updating a post, invalidate the post query. The same applies to the Use Saved Post. The queries are automatically invalidated when using the mutation. No need to manually refetch the post query. On submit is now just save post.

Get rid of React. Import our Query Cache... from our index. And here when we delete a post, we also want to invalidate the post query because you don't want to see that post sitting in that list again. So if we come in here and hit Delete Post, it's going to invalidate this list of posts and that goes away.

And let's do the same exact thing for our Use Saved Post. So it's just a patch here. I'm going to get rid of all of that. And we'll grab our mutation structure again. We'll just copy and paste it in there. And instead of deleting, we're going to patch. And we get values on Update. And we update the values to our endpoint. Same thing when we update a post. We want to invalidate these queries. And the nice thing is that this will not just invalidate the main post query, but it will also invalidate one if it was like an individual ID, which is fantastic, because we are updating an individual post here. So if we come into our test two, we got to define these mutation. My bad. Auto import. Get rid of react. We need to import our query cache again. Okay, so now we come into save post. Let's reload here. Let's go to our admin. Let's go to test two. I will update this to a bun—I'll just say updated. Hit save post, and it will refreshing the background right there. It says post query.fetch is not a function. Post query dot fetch. Oh, so we have an optimization that we get to make here. If we go to the post page in the admin, you'll notice we're doing that same thing that we don't have to do anymore. We don't have to manually refetch this post query because it's going to happen with our mutation now. So on submit is really just save post now. Let's go down here and do that same thing.

QnA

Handling Questions and Optimistic Updates

Short description:

Is there a situation where a used mutation is used without any on success callback? Yes, if you don't care about invalidating anything after a mutation. Should you use a direct import for query cache or use React context? In the future, a query cache use query cache hook will be available, but for now, circular dependencies should be fine. You can get the query cache from here if needed. Can you hook onto the mutation to minimize UI lag? Yes, using an optimistic update. How can you prevent partial matching when invalidating queries? Use the 'exact' option with a value of true. How do you keep track of query keys in a large app? Move them into a constants file. React Query dev tools allow you to see and manage queries. Inactive queries are marked as stale by default. You can open the query stack by clicking on a query. Deleting posts no longer causes a full page reload. Optimistic updates improve performance. The final code will be pushed and shown.

Let's see, a few more questions. It says, is there a situation where a used mutation is used without any on success callback? Yeah, sure. If you don't care about invalidating anything in your application after a mutation happens, you don't have to use on success. Do you recommend the direct import for query cache you're doing here, creating a circular dependency in files, or would be better to use React context to pass it around? So in the future, you will be in the future, you will be probably, well, how do I put this? In the future, there's going to be a query cache use query cache hook that comes from React query, but you'll get it that way instead. So, yes, in the future, that's the way it's going to be. For today, you know, circular dependencies used to be difficult to work around, but honestly for now it should be fine. I wouldn't worry about it too much. And if that's an issue, then you could always just get the query cache from here if you want and just not do the new way of doing things. But I'd suggest to follow the documentation as closely as possible.

Whoops, I think I lost my query cache import. There we go. Do you read? So can you hook onto the mutation is sent to minimize UI lag? Can you hook on when the mutation is sent to minimize UI lag? I think I know what you're asking, and yes, it's called an optimistic update, and we're going to do that here in just a second. If I for whatever reason wanted to prevent this partial match, how could I do that, other than giving them different IDs? When you do invalidate queries, you can pass options right here, and you can say exact true if you do not want to do sub matching. There's a lot of options to invalidate queries. So make sure you go check those out. In a large app, Redux makes it easy to see your state tree because all the reducers are eventually combined. How do you keep track of all your query keys besides searching through your code base? Well, one of the good ways is you could just take all of your query keys and move them into a single file, a constance file called query keys. That way you know what all of them are, and you're not stepping on each other's toes and mutating them all the time. Also I almost forgot to show you this. We need to go back to our index file, and we need to import the React Query dev tools from React Query Dev Tools. And we're just going to render React Query Dev Tools right here. Holy smokes. Look at the bottom left hand corner. There's an awesome React Query logo. And now you can open it up and see everything that's going on. So if we go back to our home screen, go to blog, you can see there's the blog post, right, the blog query. And we can open it up and check out everything about it. The data, the actual query object. We can re-fetch it manually in the background. We can remove it so that when we come back, it's in a hard loading state. Lots of cool stuff. You can see how many queries are fresh, fetching, stale, inactive. You can sort them. You can do all sorts of cool things. And you can see as we go through each one of these individual posts that it gets added to the query cache, and they're considered fresh still. So, yeah, the React query dev tools is awesome. It really changes how you do things. Okay. We got to keep going. We're already at an hour. Shoot. It's okay. I'm just super excited. I want to show you guys as much as I can. So we got the dev tools in. We got rid of our we got rid of the save post stuff, so now whenever we save a post it's going to invalidate all the queries. And what's nice is you can see how some of these are grayed out right here. It means they're inactive. When you invalidate queries, it doesn't invalidate, it doesn't cause inactive queries to go refetch because these aren't being used on our screen right now. It'd create a lot of bandwidth if we went and tried to refetch all those queries. We don't need to. We can just refetch them when we get to them, so it'll just mark them as stale. So this invalidate queries will just mark inactive ones as stale by default so that when they render again they'll refetch in the background. Pretty cool. How did you open that query stack? I just clicked on one of the queries inside of here. So if we go to this individual post here, there's this query object, but we can look at the state observers and all that good stuff. So here's the actual data for this post. Pretty sweet. So that works now. All of our mutations are changed over, including delete post, so already things should be feeling a little bit better. Like when you click on stuff that you've already seen it's going to be a lot faster. You know, we got rid of some code when you delete posts, like if we come in to test five, delete the post. It doesn't reload the whole page, it just kind of removes it right there. We don't get a weird – we also don't get a weird loading state like we did when we add a new one right here. Before you'd click this button and the whole page would go into a loading state, but now you just get this little background reloader, and it appears. It feels really nice. Okay, let's do some optimistic updates really quick. Yes, I will be pushing the final code from this workshop. And, in fact, yes, I will show you. That's awesome.

Exploring Optimistic Updates in React Query

Short description:

This part explores optimistic updates in React Query. It covers how to handle optimistic updates for deleting posts and saving posts. The process involves filtering out the deleted post and updating the query data. The useSavePost hook is used for optimistic updates when saving posts. The onMutate function cancels outgoing queries and sets the new values for the post. If an error occurs, a rollback function is called to revert to the previous post version. To ensure data synchronization with the server, queries are invalidated and refetched. The useCreatePost hook handles optimistic updates when creating new posts. The existing posts are retrieved, and a new post is added to the list. The query data is updated, and a fake ID is assigned to the new post. This part showcases the power of optimistic updates in React Query.

I think this is the final code he's working towards. Maybe slightly different from doing it live. Oh, you found my pull request. Good job, Jordan. Yeah, we'll have to see how they differ. I might show you a few things in the pull request that I didn't do here live. Yeah. Let's get to it.

So let's see, instead of writing all this code by hand, just for the sake of time, I'm going to be bringing some of it in that I've already written. So let's go to, like, the use-delete-post right here. What's the best way to do this? Yeah, I'm just going to be pulling it from this PR right here. So source, use-delete-post.

Okay, so optimistic updates. Optimistic updates are how you can show your users kind of like you fake them into thinking that their operation they just made was instant, even though we're still waiting on it. And this is what that looks like. So this is an optimistic update right here. And we can't really do a lot of optimistic updating with deletes because of the way this app is architected. But so we don't even have the onError, honestly. This onSuccess right here, what we do is when we delete, successfully delete a post, we grab the previous posts that are currently in the list right here. And we go and we filter out the one we just deleted, right? And we get rid of it. And then we manually use setQuery data on the posts query to basically set the new posts list with the one that we just deleted gone. And then to make sure that everything kind of equalizes, we do this invalidate queries posts. So watch what happens when we delete test7, OK? So we delete the post. When we come back, test7 is already gone. That's because we took it out right here. We went and got the existing post data, filtered it out, and then set it. Pretty sweet. This is kind of like a really easy win for deleting, like for an optimistic update on a delete. It gets a little more intense with something like useSavePost. So let's do useSavePost. This is what an optimistic update looks like in useSavePost. A little more involved. So be prepared. I'm going to get rid of this. So it's the same call right here. We have this function called onMutate, which just gets called when the mutation starts. So what we do is we have to cancel any outgoing queries, so if we come in here to Test 2, and, you know, we're going to update this. When we hit save post, this function is going to run. So we cancel queries. This is to make sure that we don't get any weird side effects happening with our optimistic update. We go and grab the existing post from the query cache using the query key that, you know, that we have designed for our application. And then we are going to set that post's data to the values we're sending to the server, kind of like, hey, I bet this is going to go through and work, so let's just kind of short circuit and, you know, manually set this query's data to the new values. And then right here, we return this rollback function. And this is a function that, should anything go wrong on the server and an error happens, we get that rollback function right here in onError and we call it. So if an error happens, it's going to call this rollback function and set it back to the old version of the post, which is pretty cool. So it looks like this. When we save post, it's just already done. We change the name or whatever, we hit saved, see how it automatically updated the title up there, test, whatever, saved. So that is this optimistic update happening. And if it were to fail, it would roll it back to the old name. And then on success, I know that we just did an optimistic update. We could just leave it alone. But you still do not have 100% certainty that the data that we put in there is the actual data from the server. And that's why you come in and you invalidate queries with the actual to go and refetch the actual data from the server. So even though that we update the name here and hit save, it still updates in the background to make sure that we're totally in sync with the server. React Query is all about that, just being in sync. We've got one more really quick. We'll do the useCreatePost, which is kind of a fun optimistic update. It's probably the most intense one useCreatePost. It looks exactly the same, except for we have this little heavier onMutate function. If we come back to the admin, what happens when we do test7 and we add this? Right now we would have to wait. I haven't saved this file yet, so it's using the old version. We have to wait for that new post to come in before we see it. What this is going to do is it's going to go grab the existing posts, so this list right here. And then we are going to add on a new post to this list right here. Set query data. We're going to take the old post and we're going to put a new one in. We're going to give it a fake ID for now. And I put in IsPreviewTrue because I'm going to show you how to do admin index. I'm going to show you a nice little user experience thing.

Exploring Query Prefetching with React Query

Short description:

We can prefetch posts that we're seeing on the screen to improve loading time. React Query makes query prefetching easy and efficient. By using the `prefetchQuery` function from the query cache, we can prefetch posts in the background. This reduces the hard loading indicator when clicking on a post for the first time. The `prefetchPost` function in the `usePost` custom hook handles prefetching. It uses the same query key and fetch function as the `useQuery` hook. Prefetching queries is a powerful feature provided by React Query, enhancing the user experience by optimizing data loading.

So it's going to have UsePreviewTrue as well. So watch what happens here. If we add Test 8, it's automatically going to get put in the list. Boom, immediately. And then it goes and refetches and everything comes back fine, and this Test 8 is now the actual data from the server because on success we invalidated this post query to go and refetch. Again, we return a rollback function just in case things go wrong to put it back to the old posts.

Now, if we do Test 9 and do some body, I'm going to save this button and click on Test 9 really quick. There we go. We just got into a broken state because we just clicked on something that has our fake ID. It's not going to work. What we can do is in the Admin Index we can come down to our link right here. And we're going to just add this nice little pack here. If we come back to the Admin. We say if the post is a preview we're going to make it kind of like fuzzy, like kind of translucent, and then we're going to put pointer events None so that we can't actually click on it. So we'll do test ten. Put something in there. Watch test ten appear. It's nice and faded. When it becomes real then it will be, you know, all of the style disappears. How fetching cool is that? Aw man. I got a good question here. It was a while back, but what are the differences between Apollo and React query? It looks like the concepts with the hooks and the caches the same. Well, first of all, React query is agnostic so you don't have to use GraphQL, but you can. Second of all, I think the API for React query is a lot better. You know, the API for optimistic updates and invalidations and managing the cache, Apollo, I've used it, and it was insane. I hate to say it, but I just think it was... it's just so complex, and it's so tied to GraphQL too that if you want to do just kind of quick and simple things with it, I'm sure that if you're running Apollo top to bottom on your stack, it's really great. But for most projects, honestly, I don't think you need it. You can get away with just using something like GraphQL request, which is just an NPM package that just is like Axios, but for GraphQL. And we could swap out all of these calls in our use. We can swap out all these calls right here from Axios to just use GraphQL, and it feel exactly the same, to be honest. You're not gaining much by using Apollo or a normalized solution. Maybe if you're using Relay. But even then, I think that it's it's such an over-engineered solution that sometimes it can be more pain than it's worth. Unless you're a massive organization like Facebook or GitHub or something, it's probably a bigger pain than it's worth. That's just my opinion. Also, Apollo is huge. Yes. I know it's getting smaller, but I think they just barely got the new version down to like 40 kilobytes or something like that. React queries only like seven. So it's a lot smaller and faster. So where do we go from there?

Okay, so optimistic updates are done. Like any mutation we can make in here is going to be optimistic. You know, we've got the title automatically saving. We can delete the post and it's already gone out of the list. That's pretty sweet. Optimistic updates are great and now we can take kind of that same concept of optimistic updates. And let's move back to queries. And how could we do like optimistic querying? Another form, another way to say optimistic querying is like query prefetching. Right. So query prefetching is so easy with React query. It would almost be like you'd have to be crazy not to implement this. What we're going to do is I'm going to go to the blog index, the main blog page. OK, right now when we click on one of these posts, we get a hard loading indicator. If we haven't if we haven't ever seen that post. Right. Get that low indicator. Wouldn't it be sweet if we could just prefetch the posts that we're seeing on the screen? I think it would be so on this post styles right here. On this like post thing that we hover over. I'm going to show you a cool little hack here. It's not a hack, actually, it's legit. We're going to do on mouse. Enter. And I'm going to call prefetch posts, prefetch, post doesn't do anything because we haven't built it yet. But it's going to come from the use posts. Custom hook. So let's go to the use post, custom hook and let's build that export const prefetch post. And we give it a post ID and it's going to use the query cache.prefetch query. And we're going to use the same query key that we used here, posts and the same function fetch posts. So it almost looks exactly like use query, right? Except for we're just prefetching it.

Optimizing Data Fetching and Using Initial Data

Short description:

We can optimize data fetching by prefetching posts and using initial data. By prefetching posts, we reduce loading time and provide a better user experience. We can also use initial data from the top-level index call to populate some data for an individual post. This allows us to display preview data and improve performance.

So let's import the query cache from our circular dependency index file. Okay, now I'm going to open up the network tab for this one. Let's reload. Okay, so we fetch posts, watch as I hover over these other ones. Oh, I'm sorry I put totally put it in the wrong file. I know you can't talk, but you should have told me. On mouse animated pre-fetch padding is also used by frameworks like Gatsby and Next, right? Yeah it can be, I don't think it's default, but it can be. Sorry this pre-fetch post doesn't need to be in here, it needs to be in the use post custom hook. My bad, so it's fetch post post ID. It's fetch post post ID. And we're gonna get our query cache from the level up. And now on our home page we're not getting pre-fetch posts from that, in fact we could just let this autofill pre-fetch post. Come on post, ah I have to manually import it, come on. Use post, kay check this out. We hover over one of these items and it pre-fetches it. Oh posts query data map is not a function. It's because I forgot to change the key. Pre-fetch query needs to be posts and then post ID. Just like this, let's try again. Kay I promise it's gonna work this time. Hover, one, two, four, whatever. You hover over any of these and now when we go to it, well it should be loaded already. We might have run into a bug. Let's try again. We hover over it, we click on it. Now I know why this isn't working, is because these need to be strings, that's why. So when you, you have to make sure that your query keys are always consistent and something that happens is when you put a number into the URL and then bring it back out with React router it becomes a string, which kinda is not cool. So you need to make sure that you're casting some of those things. So let's go back, let's retry this. I know I said it should absolutely work this time, but it's programming. So prefetch, click on it. Booyah! It was immediately available, and if your API's really fast it could probably be instantaneous. And even if we hover over it and click on it, the loading screen will only be there for just a split second, because it's already fetching in the background. We could even get really aggressive with this if we wanted. And instead of doing on mouse enter, we could legit just do prefetch post. We could do React use effect, we could make it really expensive and just be like, let's see, post query.data. And if it's there, we four reach over it. Post. We prefetch the post ID. And we could just do that on mount like, let's see if it, boom, it just prefetched everything. So it's just like already done for everything. You can see how that would get expensive. So we probably don't want to do that for everything. So that's one optimization. The other cool optimization you can do is if you don't want to prefetch, we can actually use the data that we get from the, we can use the data from the top level index call to all the posts to populate some of the data for an individual post. We can use something called initial data. It can be a function, and the initial data for an individual post, we could say query cache.get query data and just go grab our main posts query. So posts. And if it's there, we're going to find the post that has the same ID as our post ID. And we're gonna use that as initial data. Now you see how these have a little ellipses next to the bodies, it's because they're previews, right? You usually do this with like an index call. You just get like a little bit of the information, watch what happens when we click on this right here. It'll have that ellipses for initial data. Come on. Initial stale, true. We're not prefetching. So it should work. Hmm. Again, running into a little bit of a bug here. Use query. Initial data. Query cache, get query data posts. Find. Oh, I'm using a triple equals. Huh. Dude, that's awesome. Thank you, Alexander. You guys are great. So now when we come into blog and click on it, you'll see that it loaded it with kind of that preview data. You have that ellipses right there.

Caching Data and Handling Mutations

Short description:

React Query provides a solution for caching data between page loads using local storage. By hydrating the query cache with the stored cache from local storage, we can eliminate hard loading screens. The cache is automatically updated whenever the query cache changes, ensuring data synchronization. Mutations do not have built-in retry, unlike queries which retry automatically. React Query is actively exploring ways to cue up or store offline mutations for later submission.

But then we're refetching it in the background so it kind of loads in eventually. So yeah, okay, a few questions really quick. Very cool, very cool. GraphQL request. Good bug to know. String type coercion can be handled by the library. It could, but it could also be unsafe. Like to try and do that for people it could be, it could create more bugs than actually just doing it the right way anyway.

Do you know when in a case where you shouldn't use React Query? Yeah, when you're not using server state. If you have a lot of client state then you can use React Query and Redux. You don't have to pick, you can use both. How do you go about handling shaky internet connections? Well, this is really cool. I'm not gonna disconnect my internet, but I will go into the network and I'm going to simulate offline and then I'm gonna simulate online and watch the top right-hand corner. It automatically re-fetches when your client comes back online, when your client connection comes back online, so sweet! Okay, so there's like some prefetching there. So, honestly we have only about eight minutes left, so I'm trying to think like what the coolest thing would be to show you before we are done. I'm just gonna look at the, I'm actually gonna look at the PR here and just kind of see if there's anything. Let's say I showed you the global loading indicator, all the optimistic updates for mutations, that's really cool. I showed you initial data, which is awesome. In V3, you're gonna be able to do prefetching and initial data together, so it's even faster, like even better looking. Let's see, use Save Post, Index. Oh, that's one that I definitely wanna show you, but I'm gonna save it till the very last cause it's really cool. Good, good, good. We could also add our prefetching to our admin. We would just have to put this prefetch post on mouse enter logic into our admin right here on this link, so that when they hover over it, it will prefetch there as well. And, okay, the last thing I think I'm gonna show you guys before we do a little bit of Q&A is check out this. This is, you know, we're working on an official solution to do this, but for now, you can kind of play around with it. Full disclaimer, I don't know how performant this is going to be in a production application, but this is pretty sweet. So before I do this, I'm gonna make sure that we're not doing that weird... Okay, yeah, we're not... I actually like the experience of the initial data rather than the prefetch post. Both of them are in the final repo, but I like it, I like it without the prefetching to be honest. So right here when we go to our blog, if we reload the page, right, we get a hard loading state no matter what, and we can't prefetch that. If somebody hits, comes to your home screen, you can't prefetch that into the user's browser, but we can cache it. So I'm just gonna like... I'm gonna write this by hand because I think it has a good... I think it has like a good kind of punchline to it. So check this out. We're gonna call a function called, we're gonna call a function called restoreCache. Here is the restoreCache function. What we're gonna do is we're gonna to make sure that we have access to local storage. And if we do, we are going to go get the cache from local storage, cache equals localStorage.getItemQueryCache. It can be whatever we want. And then we're gonna say, if that cache is there, we are going to use React queries hydrate method, which comes from React query slash hydration package. What? The hydrate method, and we're gonna give it the query cache that we created right here. And then we're going to parse out the cache that we just grabbed from local storage. So we're saying, put this cache into our query cache, hydrate it. And then we're going to grab our query cache that's on our screen, and we're going to subscribe to it. And anytime that it changes, which could be often, this is where I'm not sure about the performance implications, which is why we're building an official solution. But until then, we can definitely play around with it. We're gonna do local storage set item, query cache. We're going to stringify our dehydrated query. Cache and dehydrate comes from hydration. Holy moly, so that loading symbol, that loading screen that we just saw was the last hard loading screen we're going to see. I don't know why it's asking me if I want to translate. Probably because it's in Lorem Ipsum. But look at this, if we reload the page, it's just there, whatever we last saw, whatever was in our cache last, it's just there. So you can cache your cache between loads in local storage, if you want. So a few more questions. What about mutations? Will they also be cued up somehow and submitted when the internet comes back? No, they won't. You know, for most applications, that's not something that people usually need, but I realize that for some applications it is. So we are currently looking into ways where we can cue up or store offline mutations and replay them. But that's gonna come in a much later version of React Query. So we're looking into it. Is there anything in React Query already persisted to local state? In case people close their browser and come back. Well, I think I just answered that question. Booyah. Will it also try to retry the operation of mutation shells for a shaky internet connection? So mutations do not have built in retry. Queries do, because queries are non-destructive, meaning as many times as you try and query, it's not gonna change anything on the server. So queries will retry automatically three times with exponential back off, which is pretty cool. Mutations are different though.

Handling Mutations and Comparing React Query

Short description:

If you have retries on mutations, you can quickly get into situations where it thinks that it erred, but it didn't. The query cache instance can be shared between multiple micro front ends. How would you say that React query compares to SWR? The React Query docs apply to everything, whether you're using GraphQL or REST or GRPC or whatever. Is there any guide on how to test those features? The best guide for testing React Query is going to be looking at the tests for React Query itself. Is there an integration test mocking library for React Query? React Query is an open source project. Consider taking the React Query Essentials course. Make sure you go to tanstack.com and sign up for my newsletter.

If you have retries on mutations, you can quickly get into situations where it thinks that it erred, but it didn't. So it retries again, and then all of a sudden, the user has created a ton of posts because you had automatic retries on. So if you want automatic retries for your mutations, you're gonna have to build that in yourself with some effects, because that is a big liability that React query doesn't, I don't want React query to handle.

The query cache instance can be shared between multiple micro front ends. It can, but if you're crossing a serialization threshold, so like if you're going between a worker thread and the main thread or between SSR and the browser, all that stuff, then you have to use hydration, all the hydration APIs to share the cache. Like kind of keep the caches in sync, if that makes sense. But as long as it's within the same thread and the same instance of JavaScript, you can share the query cache across multiple front ends.

How would you say that React query compares to SWR? Well I'm glad you asked. In fact, I'm gonna bring up something that I built, I've been working on for a while. This is the comparison page that's in the React query docs, right here. So here's React query on the left and SWR down the middle. And just some basic differences up here at the top. But you'll see that, sure. You need to save three kilobytes, two or three kilobytes. I understand that that's nice. But, you don't have dedicated Dev tools, you don't have bidirectional infinite queries, which we didn't talk about today, you can't do lagged query data, which is basically makes it feel like you have suspense, but without suspense, which we didn't talk about today. You don't have selectors, render optimizations, or garbage collection or mutation hooks or pre-fetching APIs. Like some of this stuff is partially implemented, right, but you don't have things like query cancellation. There's so much we didn't even talk about today, but the partial query matching doesn't exist. They don't, there's no such thing as stay time, which is super powerful for if you're actually building a real application, scaling it, you know, they don't have any hydration APIs. And yeah, and there's Apollo over there on the right as well. So it looks like Apollo is 32 kilobytes F-Y-I. So yeah, those are the differences. And also I just think the API is just nicer and cleaner. It's just much easier to work with, in my opinion.

That's really cool. It makes me seriously consider using React Query for a new large scale application with GraphQL over Apollo Relay. We don't use either in our organization yet. Do you have resources on helping with that comparison? Same, here's Apollo over here, and some of the things that it can do that React Query can't, like normalized caching, and some things that React Query can do that Apollo can't. There's more details down here if you wanna dig into what they actually do. I would say for 99% of people, you do not need to use Apollo, unless you have reasons to use something like Relay. It's more of a question of, should I use React Query, or should I use Relay? And that's a whole different other discussion. Yeah, so the best resource, like honestly, the React Query docs are applied to everything. They apply to everything, whether you're using GraphQL or REST or GRPC or whatever. You know, they apply to everything. There is a nice little GraphQL page here that says, hey, it's all the same if you just have to use a GraphQL client. And we have an example here using GraphQL Request that shows you it looks exactly the same except for we're using, we use GraphQL to go grab the host data instead. It's really all the same. Works great.

Is there any guide on how to test those features? The best guide for testing React Query is going to be looking at the tests for React Query itself. We don't have a guide for it yet while working on it. But there's lots of guides and concepts over here and there's lots of examples to help you out as well. And there's a very thorough API reference and you have global search as well for the entire docs. So sweet.

Few more questions then we gotta wrap it up. Is there an integration test mocking library for React Query? No, if you want to mock your stuff you can manually create a query cache with queries inside of it. Again that's something I can't go over today but we do that ourselves inside of React Query. Yeah absolutely. So before I go, so React Query is an open source project. I do it in my spare time and I'd really like to do more open source. I have a lot of open source libraries but I'd like to do more. If you really are interested in learning more consider taking the React Query Essentials course. You can go to learn.tanstack.com and find it that way or it's right on the homepage of React Query. It's got a lot of great content basically just more of what you saw today just a little bit more in depth and going through almost all of the features. That would really help me out. If you live in a different country I even offer PPP or Purchase Power Parity Discounts. So purchase power I guess that's on learn.tanstack. Yeah, I can't pull it up but it's a Purchase Power Parity Discount. So if you live in a different country where the dollar is kind of out of whack compared to your currency I give you a discount which is pretty sweet. If you're going to school right now and you don't have a job you can DM me and I will look into a discount for you. If you're on a big team and you want team discounts I sell team discounts for React Query Essentials. So there's really a lot of awesome stuff to find in here. And when new versions come out I'm going to be doing my best to keep that course up to date. Other than that, make sure you go to tanstack.com and sign up for my newsletter. I really do not send out a lot of stuff unless it's really important. I've only sent out two or three newsletters so far. Tanstack.com, learn.tanstack.com. Here's the React Query docs if you guys want them. And, yeah I think that's it. Are there any other questions that I can answer before anybody, before we go? I really wish there was a way that I could say hi to everybody.

Wrapping Up and Final Remarks

Short description:

Thanks for tuning in and listening. I hope you guys learned a lot. The best way you can help me out is by sharing your positive experiences with React Query on social media. It has been a game-changer for many people in their startups, companies, and job searches. You are free to leave now, but I'll be available in Slack for a few more minutes if you want to chat. It was great meeting and talking to all of you.

Um, let's see, stop video, or no. How do I stop sharing my screen? Stop share, start video. Yeah, I don't know if there's a way to see everybody. But honestly, thanks for tuning in and listening. I hope you guys learned a lot. And honestly, the best way you can help me out, if you're not interested in the course, or you already know React Query, just get on Twitter or get on whatever your social network is, and just gush, I really like it when people gush about React Query. I have had so many people tell me that it's changed their trajectory for their startup, or their company, or getting a job, or anything like that.

So, yeah, that's it, you guys are free to go. But if you wanna hang around, I'll be hanging around a little longer in the Slack to chat. Probably five or ten minutes. So, it was great to meet you all, and talk to you.

Watch more workshops on topic

React Summit 2023React Summit 2023
170 min
React Performance Debugging Masterclass
Featured WorkshopFree
Ivan’s first attempts at performance debugging were chaotic. He would see a slow interaction, try a random optimization, see that it didn't help, and keep trying other optimizations until he found the right one (or gave up).
Back then, Ivan didn’t know how to use performance devtools well. He would do a recording in Chrome DevTools or React Profiler, poke around it, try clicking random things, and then close it in frustration a few minutes later. Now, Ivan knows exactly where and what to look for. And in this workshop, Ivan will teach you that too.
Here’s how this is going to work. We’ll take a slow app → debug it (using tools like Chrome DevTools, React Profiler, and why-did-you-render) → pinpoint the bottleneck → and then repeat, several times more. We won’t talk about the solutions (in 90% of the cases, it’s just the ol’ regular useMemo() or memo()). But we’ll talk about everything that comes before – and learn how to analyze any React performance problem, step by step.
(Note: This workshop is best suited for engineers who are already familiar with how useMemo() and memo() work – but want to get better at using the performance tools around React. Also, we’ll be covering interaction performance, not load speed, so you won’t hear a word about Lighthouse 🤐)
React Advanced Conference 2021React Advanced Conference 2021
132 min
Concurrent Rendering Adventures in React 18
Top Content
Featured WorkshopFree
With the release of React 18 we finally get the long awaited concurrent rendering. But how is that going to affect your application? What are the benefits of concurrent rendering in React? What do you need to do to switch to concurrent rendering when you upgrade to React 18? And what if you don’t want or can’t use concurrent rendering yet?

There are some behavior changes you need to be aware of! In this workshop we will cover all of those subjects and more.

Join me with your laptop in this interactive workshop. You will see how easy it is to switch to concurrent rendering in your React application. You will learn all about concurrent rendering, SuspenseList, the startTransition API and more.
React Summit Remote Edition 2021React Summit Remote Edition 2021
177 min
React Hooks Tips Only the Pros Know
Top Content
Featured Workshop
The addition of the hooks API to React was quite a major change. Before hooks most components had to be class based. Now, with hooks, these are often much simpler functional components. Hooks can be really simple to use. Almost deceptively simple. Because there are still plenty of ways you can mess up with hooks. And it often turns out there are many ways where you can improve your components a better understanding of how each React hook can be used.You will learn all about the pros and cons of the various hooks. You will learn when to use useState() versus useReducer(). We will look at using useContext() efficiently. You will see when to use useLayoutEffect() and when useEffect() is better.
React Advanced Conference 2021React Advanced Conference 2021
174 min
React, TypeScript, and TDD
Top Content
Featured WorkshopFree
ReactJS is wildly popular and thus wildly supported. TypeScript is increasingly popular, and thus increasingly supported.

The two together? Not as much. Given that they both change quickly, it's hard to find accurate learning materials.

React+TypeScript, with JetBrains IDEs? That three-part combination is the topic of this series. We'll show a little about a lot. Meaning, the key steps to getting productive, in the IDE, for React projects using TypeScript. Along the way we'll show test-driven development and emphasize tips-and-tricks in the IDE.
React Advanced Conference 2021React Advanced Conference 2021
145 min
Web3 Workshop - Building Your First Dapp
Top Content
Featured WorkshopFree
In this workshop, you'll learn how to build your first full stack dapp on the Ethereum blockchain, reading and writing data to the network, and connecting a front end application to the contract you've deployed. By the end of the workshop, you'll understand how to set up a full stack development environment, run a local node, and interact with any smart contract using React, HardHat, and Ethers.js.
React Summit 2023React Summit 2023
151 min
Designing Effective Tests With React Testing Library
Featured Workshop
React Testing Library is a great framework for React component tests because there are a lot of questions it answers for you, so you don’t need to worry about those questions. But that doesn’t mean testing is easy. There are still a lot of questions you have to figure out for yourself: How many component tests should you write vs end-to-end tests or lower-level unit tests? How can you test a certain line of code that is tricky to test? And what in the world are you supposed to do about that persistent act() warning?
In this three-hour workshop we’ll introduce React Testing Library along with a mental model for how to think about designing your component tests. This mental model will help you see how to test each bit of logic, whether or not to mock dependencies, and will help improve the design of your components. You’ll walk away with the tools, techniques, and principles you need to implement low-cost, high-value component tests.
Table of contents- The different kinds of React application tests, and where component tests fit in- A mental model for thinking about the inputs and outputs of the components you test- Options for selecting DOM elements to verify and interact with them- The value of mocks and why they shouldn’t be avoided- The challenges with asynchrony in RTL tests and how to handle them
Prerequisites- Familiarity with building applications with React- Basic experience writing automated tests with Jest or another unit testing framework- You do not need any experience with React Testing Library- Machine setup: Node LTS, Yarn

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

React Advanced Conference 2022React Advanced Conference 2022
25 min
A Guide to React Rendering Behavior
React is a library for "rendering" UI from components, but many users find themselves confused about how React rendering actually works. What do terms like "rendering", "reconciliation", "Fibers", and "committing" actually mean? When do renders happen? How does Context affect rendering, and how do libraries like Redux cause updates? In this talk, we'll clear up the confusion and provide a solid foundation for understanding when, why, and how React renders. We'll look at: - What "rendering" actually is - How React queues renders and the standard rendering behavior - How keys and component types are used in rendering - Techniques for optimizing render performance - How context usage affects rendering behavior| - How external libraries tie into React rendering
React Summit Remote Edition 2021React Summit Remote Edition 2021
33 min
Building Better Websites with Remix
Top Content
Remix is a new web framework from the creators of React Router that helps you build better, faster websites through a solid understanding of web fundamentals. Remix takes care of the heavy lifting like server rendering, code splitting, prefetching, and navigation and leaves you with the fun part: building something awesome!
Vue.js London Live 2021Vue.js London Live 2021
34 min
Everything Beyond State Management in Stores with Pinia
Top Content
When we think about Vuex, Pinia, or stores in general we often think about state management and the Flux patterns but not only do stores not always follow the Flux pattern, there is so much more about stores that make them worth using! Plugins, Devtools, server-side rendering, TypeScript integrations... Let's dive into everything beyond state management with Pinia with practical examples about plugins and Devtools to get the most out of your stores.
React Advanced Conference 2022React Advanced Conference 2022
30 min
Using useEffect Effectively
Can useEffect affect your codebase negatively? From fetching data to fighting with imperative APIs, side effects are one of the biggest sources of frustration in web app development. And let’s be honest, putting everything in useEffect hooks doesn’t help much. In this talk, we'll demystify the useEffect hook and get a better understanding of when (and when not) to use it, as well as discover how declarative effects can make effect management more maintainable in even the most complex React apps.
React Summit 2022React Summit 2022
20 min
Routing in React 18 and Beyond
Concurrent React and Server Components are changing the way we think about routing, rendering, and fetching in web applications. Next.js recently shared part of its vision to help developers adopt these new React features and take advantage of the benefits they unlock.In this talk, we’ll explore the past, present and future of routing in front-end applications and discuss how new features in React and Next.js can help us architect more performant and feature-rich applications.