All About React Query (with Tanner Linsley) — Learn With Jason

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everybody and welcome to another episode of learn with jason today on the show we have the maker of the tan stack himself tanner linsley thank you so much for joining us good to be here uh so i i think that it for those of us who work in react we've probably heard your name um across a bunch of different places right like you're in addition to react query um you've also made react table and a whole suite of tools that show up all over the place when you're looking for for things to you know just make your react workflow uh easier i guess less less painful um but for those of us who are not familiar with your work do you want to give us a little bit of a background sure um so i have a startup called nozzle and that's where we basically reverse engineer google search rankings okay um provide that data to people in building nozzle over the last five or six years i've run into a ton of things with react that just like you said were a little painful um you know the first library that i built that got some traction was react table um and because building tables seems like something that should be easy but it's not uh especially when it gets like into like the data grid stuff yeah you know sorting filtering pagination thing kind of blows things up in in your face absolutely so that's been around for quite a while uh i have a couple of other small repos that i've built here and there react virtual it's just like a little virtualization one uh i did a lot of work on chart js um i even had a little stint in uh doing static site generation with react static but i don't maintain that anymore yeah um but my latest one is react query that it just technically turned a year old a couple weeks ago um but it's kind of been it's been in the you know in the spotlight for about eight months so yeah and so react query um i guess to to maybe start from from like the broad view right like why so if i if i'm looking at querying data in react you know i the browser has a built-in fetch api so what what am i doing with react query that is is different from the fetch api or i guess maybe a better question is what is the fetch api not doing that react query is helping me with that's a good question so you're using the fetch api using the browser cache like you can still use caching headers and whatnot and like save on bandwidth and speed right but that that concept of caching doesn't carry itself all the way down to the user experience in your application like even if you have to even if you re-fetch data and it's like right there at the browser cache you know a lot of the systems we have in place in react they're they're not going to like i hate to use this word but they're not going to suspend while that happens and make it feel like it's just there instantly right um so first and foremost react query is kind of like this really light caching layer that just lives in memory inside of your application okay it it doesn't uh take over the responsibility of of you know using the right cache headers and and doing like server side and browser level caching but it's more of a cache to improve the user experience and and possibly fill in some of those gaps you know if you don't know a lot about browser caching then react query can also help you a little bit there so first and foremost it's like a ux driven library it's built to improve oh we just lost your audio for a second oh wait i think i heard you again so it's built to improve built to improve the uh first and foremost the user experience and also the developer experience in trying to work with that data nice yeah and i think that's a really important distinction too because this is um this is something that we see that comes up a lot is we talk about user experience we talk about developer experience and and sometimes a tool will over index on one or the other where the user experience is really good but oh my god is it hard to build uh yeah or you know you kind of go too far in the other way and the develop the developer experience is a dream and then the the end product is just kind of a drag to use for for the end user uh yeah finding that right balance is difficult for sure uh and so that that's uh you so you mentioned a couple things like you mentioned caching headers so so react query is gonna deal with that for us uh not technically so react query is really agnostic as to how you actually fetch your data okay the only thing that it needs to know about is a promise so uh reactor react query doesn't necessarily know about the fetch api or or any of the actual mechanisms or protocols that you're using it just knows you know about asynchronous operation that's really all that matters fascinating so does that mean that like i i get to choose my data fetching library then so if i want to use the fetch api i can if i want to use axios i can um yeah what about like server-side rendering like if i pull in isomorphic fetch will that work um yeah so the server-side story for react query is interesting um there is there it has a great story for ssr that revolves around hydration the same way that uh you know you hydrate uh the react application when it gets to the client um similarly you would basically you you fetched that data server side into a cache and then you send that cache with your html and you read it oh i got you i got you yeah so that's i i totally get that that totally makes sense well cool um so i i i have a million questions but i feel like it might be easier to just look at this right so maybe yeah go ahead and one of them too that while you were uh while you're saying oh i can use whatever i want you can also use graphql clients which kind of look at that opens up uh some interesting like architectures and discussion there so absolutely uh and and i like what i like about that is that you're not enforcing an opinion so i can hit a rest api i can hit a and it sounds like if you're not enforcing an opinion i can even hit like the file system or something theoretically yeah anything that's asynchronous uh that's cool that's super cool uh thank you for the sub ryan i appreciate it um nine months time flies so uh how about this let's uh let's switch over to pairing view because i want to see this thing in action and um while we do that let's take a second to talk about our sponsors we've got uh a uh live captioning today i really hope that i fixed this i did i fixed it okay good um so we've got live captioning happening right now by white coat captioning thank you so much to eun who's in the in the chat right now um and that is made possible through the generous support of netlify fauna sanity and off zero who all kick in to make this show more accessible to more people you can find that at lwj.dev live if you want to see the live chat we've also got the the video and the team chat playing in here so you can kind of see it all at once also make sure you go and follow tanner and he's on twitter at tanner linsley uh and we are taking a look today at react query so here's a link um all right i'm ready to roll so where should we start if we want to give this thing a spin uh let's dive into that repo you've got um it's actually set up to be just kind of the dumb little blog example that lets you add and remove blog posts and kind of look at some of these things kind of in a transitional lens a little bit so we'll start off with no react query and i can incrementally help you add in some of these these features to show you exactly how it feels to use it you know instead of just like dropping a react query example into your lap right got it okay so um let's see i haven't done anything on this yet so let me run the yeah let's just do like uh let's do a yarn install so we'll install dependencies here and then let's take a look at what's inside so we've got a an index page and see we've got like a wrapper um a sidebar admin section blog section and looks like we have our sidebar here our main here and then some routing so yep home page is a welcome page and then we've got an admin page and a blog and we can pull up individual posts in either editing mode or blog mode um cool so let's start this thing up looks like we're going to use dev yep yarn dev should do it and that's going to give us localhost 3000 if i can copy looks paste i missed a letter okay so we've got our homepage there's our welcome go to our blog we're loading we've got blog posts yep and then if i go to the blog's id we get the full content yep okay so this is simple yeah this is and this is really straightforward and as you said this is not currently using like let's look here we've got screens here's our blog index so we've got use posts and when we call use post there's a hooks library and we're loading it using axios yep okay so this looks like what i would do you know i i i've stopped using axios because it i just i i don't know i got converted to fetch at some point but i this looks very familiar in terms of how i would set up a an app if i was going to do this so yeah i think this format is pretty familiar to most people yeah you know that that have fetched data in react excellent yeah so this um and and this then i can see a couple things that i don't love like when we click through here we can see that we have that loading screen and then i click and i have that loading screen and now we've already loaded that data so the fact that it's taking that much time yeah that's really annoying um so and then let's see our admin we have the ability to edit and then it looks like if we pull up we can load and we've still got that that loading screen yeah and also if say we're on the blog post page and even in development here if we were to go to the actual json store and change one of the titles for that blog we'd either have to reload the page or like you know unmount the component and remount it to like get like to see that it should be under store.json it's just like a little json store file got it but if you were to if you were to change the title you know of that first post um it wouldn't it wouldn't make itself into the dev environment even until i really stopped doing without doing some type of refresh or under or unmount and remount you know yeah so that's himself that's also a bummer um and and you're saying so all of these things that we've just listed is kind of our list of grievances these are all things that react queries can help us with yeah essentially nobody wants to use an application where every click you see a loading bar for something yeah especially in especially when you're developing applications that can get really annoying because usually it's repeated actions you're kind of going between different places uh multiple times you know so it's like every single time you go back to the home screen you know you see this loading thing and you're like oh no so i yeah and i'm i'm just laughing because uh david korsheed's in the chat right now uh letting us know that both is loading and his success can be true at the same time and he would like for you to see him after class [Laughter] tell david that's not possible oh no impossible states are impossible um but yeah so this i think is uh this is gonna be fun right so i'm i'm ready if where where should we start like should we start on the blog page or somewhere else um let's let's start on the blog page let's and the nice thing is we don't actually need to start on the blog page we could start in that use post hook because that's really where all the logic is happening that we're going to be working with that's that's where the action is okay so i'm i'm in use posts yep so first thing you want to do is uh there's kind of two sides to react query there's queries and mutations and we're going to talk about mutations later if we can but okay for queries uh react query exports um a use query hook so you can just import one of the named imports use query which is camelcased okay from react query and then we're going to use use query to basically replace all of this code in here so okay anywhere you want let's just start by calling use query and i don't know if you have intellisense on but we can kind of get a feel for what use query takes as parameters so i in this particular code profile i don't have uh intellisense on because it the pop-up is totally fine yeah yeah so the first parameter that use query takes is some unique identifier to describe whatever it is you're fetching okay uh in the most simplistic examples it could just be a string so okay for this example i think just a string like i usually call it posts right that's what we're fetching okay and you don't even need that slash in there it could just be yeah just post got it the uh the second parameter that you're sending through for use query is going to be the fetcher function so this is the asynchronous function that's responsible for either getting your data eventually or or throwing an error okay bad happens and is this um like is it in an object or is it just like the function it's just the function yeah okay and so it returns async and then i would probably just copy this right the yeah in fact in this case uh you wouldn't even need to do the async business if you just wanted to do an inline function that returns axios.get whoops okay so i've got back oh and we don't have to await because axios.get will return a promise yep and so we can just trust that the javascript knows that uh async await is just syntactic sugar on top of promises and everything will work the way we expect exactly okay so all this state business down below here is still important but react queries use query hook handles all of that state so you can get rid of that entire reducer we're not going to need it anymore no more reducer yep uh the fetch function clearly we don't need that anymore and react query also handles all the life cycle around um all of your fetching so we don't need the effect at all in fact we don't need any of it just okay so just uh well actually i guess so the the nice thing here and i kind of cheated a little bit with this example um i used the same return structure okay for that custom hook that use query returns as well so you can literally just return use query here oh okay so then if i take this out then what we'll get is the the spread state then would be the fetch or the is is loading a success yep so use query returns like a query object and it has all of that same information on it it's got the you know the status enums for david so he doesn't have to freak out about booleans it has booleans which uh you know i prefer the booleans and and uh just so david doesn't squirm like the the booleans from this query object that you get back they are stable like they they can't get into impossible states okay um but they're so they're derived from an underlying status right and then it also gives you back like you know the data property and error property and a couple of other properties we can look at here in a little bit okay so then i'm using this and then if i go to my index page here i get my post query so i'm going to just console log this to take a look at what came back sure david david is in support of derived booleans oh that's good okay so we've got now we've got this object right so can fetch more we've got clear we've got our data so here's our post and then down here we've got um is error is fetched okay and then we've got this status so this would be the the more like if you're if you're an ex-state fan this would be kind of what you would expect right is a an enum yep status is like what is the actual state of this machine if you if you want to talk in x-state terms right yeah cool very cool and then we've got a couple functions here refetch remove uh last updated that's really nice um this is slick this is really slick and honestly a lot of this stuff in here you don't honestly don't have to interact with that much like it's all there as part of the public api but for the most part like i don't even really use much of this other than is it loading you know the data yeah just kind of stuff we're using right here and so that so with what we just did does that mean that if i click into one of these posts and then click back out to the blog i'm not going to see that loading screen anymore because it's already there precisely all right here we go moment of truth i'm clicking in so we haven't changed use post yet we've only changed the use posts so if i did this right no typos we're gonna see an instant load of the blog yep boom look at it go okay so we'd literally changed like almo we actually just deleted a bunch of code which is wonderful like that is so nice um and you know we can even look at this uh here's our our diff here we went from all of this code down to just this so just the axios code here and use query and that instantly improved the experience of of the user so this is like a dx win and a ux win here because now our our page loads better and we also had to do less work so i think everybody's happy here yeah it's it's not even like uh like a net gain it's like a double game right you're getting rid of code and you're getting better features out of it so yeah this is this is excellent so i'm i'm really into that um so then to do the same thing with the use post let's see if i can do this i'm gonna so i'm gonna import use query from react query and then down here we've got our post id and that just returns axios um and so i'm going to instead of all of this state and it looks like this one has the same return value so i get to delete all of this stuff and i'm going to return use query and we'll call it um i'll probably just use this post id right like that seems like a reasonable unique identifier i'll give you a little hint here after you get done on the key okay okay okay um and so then my second argument is to have a function that will return fetch post post id and then i can delete this bit this should just work right yep okay so what could i have improved here so uh this will definitely work like this is a unique key for the post itself right um but for the future uh we're going to add some like some organization to this key a little bit okay so you can actually pass an array as a key interesting and instead of just a string we're going to kind of create some hierarchy here so we're going to call it posts and then post id interesting okay and then do we want it that gets serialized for you no no it gets serialized for you under the hood and everything's just kind of magical okay and so what's the benefit of doing it this way versus uh another way um i'll have to show you in a minute but it comes down to how can we manually invalidate some of these queries on our page okay i got you if i were to ask you you know which one's easier to remember right this kind of this path kind of based string here or like kind of a structured object and most people are going to say oh well i can remember the structure right and it's easy it's easy to turn that into a const to somewhere in your application so i get you yeah there's some other benefits to it too uh some things like prefix matching you know like if we wanted to uh if we wanted to refresh all of the posts queries on our page uh you you know you could do something like uh invalidate queries posts and that and that's going to invalidate like every single thing that matches post prefix so it gets pretty cool i understand that's really cool um does work you know yeah so okay so um so quick note for the chat uh q a is the whole time if you if you've got a question fired off in chat um and we will we'll try to answer them in line keep in mind if the questions go too rabbit holy we'll probably push them off to twitter or something else but uh you know definitely uh definitely do that so yes the um the code about invalidation we're going to get we're going to get to that yeah but let's circle back around though i i want to see the i want to make sure that the posts or the post query is working yeah so as you were explaining the the invalidation i was clicking in right and so we can see now we've got an instant load into the post and then one that i haven't loaded yet like this test down here right it loads but then when i come back out and click back in there it is instant so now we've got this amazing uh this amazing experience of very quickly loading all of our posts and then if so i didn't ask about this and i didn't look at the code yet so i don't know if this works but in the admin section we've already updated this right because we does admin use the use posts hook to load those posts yeah it does so this should be instant and i haven't loaded this at all oh my god that is amazing i i love it when things get easy and i don't have to write any code exactly that is beautiful um this is and this is really nice because i feel like this is something that we've all dreamed about and this is something that a lot of libraries kind of promise right you you see a lot of frameworks and tooling talking about how they're gonna they're going to do this deduplication do this cash management do all these things that will make your developer experience really easy um but typically that comes with a lot of boilerplate you you feel like you're setting up a lot of stuff up front to get that to work and what's interesting here is we didn't have to do that like are we is there a context provider somewhere or like something that's keeping this stuff globally available or like how how is this actually working yeah so we're in v2 right now this is v2 of react query okay and in this particular version there is just kind of a global singleton for your app that you can just kind of start firing off queries and everything just works like okay context is not required interesting that's even cooler and it does scale pretty well um in v3 that's coming up here in a couple months probably to help out some of our larger customers um we're we are adding um kind of a provider like a query client kind of a provider okay um that it'll probably feel a lot like uh relay you know or something like that that where you provide the client to your application yeah um and it'll be using context under the hood to share that client but at the end of the day we're not we're not using context to trigger renders or anything like that it's it's kind of just all homegrown i got you okay cool so so right now i mean we're what we've we've been writing code for less than 20 minutes and we just made huge front-end improvements and also managed to delete like 50 of two files which feels pretty good um and that was just the hook versions of these files right like most people will not stop there you know if if you want to get into some of these types of features you can't just write hooks like that that use use effect and state like this is that's where people start okay where do i go from here do i go into redux do i get into mobx do i usually they search for a global state management solution that because they need global state because they need to be able to share all this cash and you share the data around right whereas global state managers themselves they're more they afford more to client state management than actually being able to manage all of these like unique server state life cycles you know okay and i think that's why people get into trouble with global state managers if you're using a global state manager just to manage like asynchronous data yeah you are basically going to end up writing all of the logic that has gone on gone into react query on your own mm-hmm and it gets it gets really messy yeah i can i can imagine uh and that that actually kind of ties to a question here um so nikki in in the chat is asking uh can you combine this with something like apollo uh or does this replace something like apollo so i would say it probably replaces apollo more than using you could use it with the apollo like the client that comes with apollo to fetch stuff from graphql but at that point i would say just use a package like graphql request you know that's just yeah hey here's or you could even just use fetch you know that's that's actually how i send a lot of my graphql request these days is the fetch api yeah it's super easy and the only thing that you're not getting with react query that you might get with something like apollo or relay is going to be the normalized database for your individual query fragments under the hood and to be honest most people don't even really know what data normalization is doing under the hood and a lot of applications don't really benefit from it or or they're not it's not crucial you know yeah so unless your facebook or you know some really complex uh application that's built on like graphql in its entire ecosystem i don't see a big reason to use something like uh like apollo or relay unless you have really really good reason to you'd be better off probably just using react query with fetch you know okay or something like graphql request in my opinion okay um and then i another question and then we'll we'll do write some more code here uh is would you use react query to manage auth state many people do um it just depends a lot on how you do authentication okay uh i have some auth state in one of my apps that's really simple and yeah i just use uh react query to kind of keep it keep it up to date okay uh there's some other i have another auth system that uses firebase auth which is a big hairy monster yes um and i i don't even bother i just have like a custom off provider for that i don't know yeah cool yeah all right well so i want to go deeper um what should we do what should we do next um let's do something that's kind of fun before we go over and like get into mutations why don't we head back to the use post hook post singular or plural the singular version okay so i'm in the singular yep and i'm i'm actually going to show you a little trick here i'm going to explain it as i do it is if that's okay yeah go ahead so remember that global object you're like where's all this stuff getting stored yes right it's getting stored in this query cache global for now and something interesting that happened is when we look at a new individual post we still get that hard loading state you know but if you notice why like we've already loaded that post in the parent query yeah that's that's true or or at least part of it right part of part of it's there uh the preview i guess you could call it so we we should be able to use that and the good news is that you can so i'm going to take this query cache that has all of our data in it or should right and one of its methods is called get query data and i'm going to pass the key for our parent query okay and that technically should give us in fact if we console log that out uh if we head back to oh sorry fetch post initial data is the function i want to run it in duh okay so if we head back to our page and let's just go visit an individual post and just see if we get something in the console okay so let's go to the blog click here pull up the console and i think this was it maybe i should take out the yeah perfect let me let me get rid of the uh the part where we're logging the rest of the post so that we can see just the one that we're looking at okay and so it's interesting we loaded in straight into it and it was undefined yeah right so and that's because it was the first page we loaded there's not much we can do there right um but if you're going from the blog into an individual post yeah so we see that data what we're going to do is we're just going to try if if it's possible we're going to try and find the post in here that corresponds to the one we're trying to fetch okay and we're going to return it as initial data so we've got the query cache and then get query data is a built-in to the query cache uh and we use the queue or the key of uh of posts which is the same one that we used in use posts here so if we change this then we would change this those those have to match yep and then when we do our find we're gonna get the the entry so this is an array right so we're just doing a find on this array and checking if the id masks are given post id so this all makes sense right this is this is kind of like what you would do with an array you would look up to see if you've got the thing um and so once we've saved this then so theoretically speaking so i'm going to refresh so i get my loading and then if this did what we want it to do we shouldn't see that first loading screen when we click into one of these posts because it's already in the cache right precisely look at that that is magical um but then if we refresh well there's something else to see a quick load you can you can see that uh the version that you were seeing on the preview though so let's go back and do the uh the initial data flow again so blog refresh okay um click on an individual one and just kind of leave it sitting there oh there we go you'll notice that there's a little ellipses from the preview right because it you know you don't want to fetch all the data for an index call and all the tests right that makes sense so we can also pass uh if you come back to the use post hook now um we're going to pass pass initial stale true and this is just a little hint to react query that what we're passing is initial data is kind of more like a placeholder okay so i load then i click there's an ellipsis and then it loads the new thing that is slick i these are patterns that like i always want to build and i just never get around to so this makes me really happy right it's really fun and you know i we're not going to do it because it takes a little bit more time but you can actually do the reverse as well so if you went into like the used posts hook you could take the data from use query right here and uh sorry the it's the multiple use posts you could take the data here and say on success i want to take this data and we could like loop through the data like this and grab the query cache and say set query data and we could do something like uh posts oh post no kidding okay and so then that would already work when we went into one basically i basically just wrote it so let's see if it all works so uh so we're going in so then would we need to take it out of yeah we can so let's take uh this initial stuff out of here okay yep so go back to the blog i'm going to refresh there's a little way this works i don't do this one as often so oh it doesn't like set query oh my bad my bad it's it's set query data here we go here we go that query data okay so i'm reloading let's try again there's our initial load and it it gave me a a load screen so i i think it didn't quite take that but and uh yeah maybe maybe we have to do the still time to be a little higher something like five seconds let's see if that see if that fixes it this flow is not as uh it's not as vetted i guess you could say and say that's okay ah it's all right it's easy enough to do it the other way right so we can yeah we have we have docs on how to do that um but uh you can see how flexible things get right absolutely how do i want to approach kind of these optimistic fetching strategies and this is just so nice that it it starts with like the placeholder and then pulls into everything and that's gonna get better in v3 too like a lot of fun things are gonna happen in v3 very cool um before we go further let's i want to show you something that's probably going to blow your mind it's going to be really fun wait hold on it gets better all right cool yeah it gets better so head back to the index page where we've got all of our routes index page where we've got our routes now this index page okay um down here uh just after really just after the wrapper uh or yeah right after the wrapper closes right after the wrapper closes okay we're gonna render a new a new component after okay um and let's see if i can find you i'm under the wrapper close here line 44. here we go so we're gonna render the component uh react query dev tools okay and then we're gonna come up top here and import that so import react query dev tools from react query dev tools okay let's go back to the app look at this oh okay and so then i'm gonna click into one of these we'll click into this one oh that's slick and then we can pop this open and see what's in it dang so this kind of gives you like the visualization of caching which is a hard concept to visualize if you think about it but on the left there you got the list of all those queries that we have been running and you can see some of them are grayed out which means they're inactive because we're not we're not subscribed to them on the page right now right right but they're in the cache and they're technically still fresh right now you've got one that's stale on your page right now post one and so we kind of start learning about this concept of stale versus fresh right out of the box react query is very aggressive okay so it's kind of always refetching your data in the background um let's go back let's uh let's go to an individual post just just pick the first post uh as an example okay and let's go into the code uh let's go into the editor right into the store editor into the store yep and and just go ahead and change the title to something random oop corgi there whoa did you see that it was okay so it showed me fetching and i have not refreshed this page and now it's live yeah i mean try it again without the dev tools open let's just see what happens dang okay and so is this like under the hood is this polling no so it's it's using your interactions as a user to kind of hint to react query when things should be up to date so uh one of those hints is a window refocus event so when you when you refocus the browser it's detecting like hey you've come back we should probably update things that are stale or need to be updated in the background got it okay and it does it without flashing loading everywhere right it's just kind of in the background updates that is super slick and i thought i was trying to like rush to get back before it finished refreshing so that's cool that it does that um on like on refocus polling can be expensive right and that's kind of what i was thinking right you can set up polling if you want and and honestly i use web sockets in in my applications to to get specific events and and tell react query to kind of invalidate things um yeah but but the on window focus stuff is is really nice without you don't have to do anything that just kind of comes with it so you just you just said something that piqued my interest and i i just want to ask about it we don't have to rabbit hole here but um you said that you're using websockets to work with react query so does that mean that i could do something like a subscription with react query kind of so react query doesn't treat subscriptions as like first class things right now um because you don't really need to the mechanics of subscriptions change a lot sure you could be using firebase or pusher or you know so many mechanisms out there um but really what matters is just can you respond to an event coming in right and with react query it's really easy you just have to have the query cache in hand and you can respond two ways if if your notification from whatever this subscription is has the payload that you need then you just use the query cache set query data you know just kind of okay update it okay everything on the page updates um if you're doing more of like a poll model where the notification is just telling you something has changed then instead of doing the set query data you just do invalidate queries and so you just tell your queries to go and refetch right and and that invalidate queries that's just a that's just something that's going to come back from the query itself or is that a is that like a like how we imported the query cache we would also import like invalidate queries as a like a named function yeah in fact let's let's get into that a little bit um do we how much how are we doing on time we've got 45 minutes we've got plenty of time perfect okay let's get into some mutations because i think some of this is going to become clear if we get into the mutation part of it yeah let's definitely uh so to look at how this works um we've got our admin page here i'm gonna pull up this test post and we'll say um what up chat and this is the best so i'm gonna save uh-oh what did i do uh that's totally fine it's uh that's expected because right now if you go to the admin screen go to the index for the admin screen admin index and you'll notice something uh on submit we're waiting for create posts to finish and before when we weren't using react query uh we had to kind of tell this post query to to go manually fetch again and we've and so that was why when we look at uh at this use post or use post i should say there's um an export uh yeah and this one we don't have anymore like we're not exporting it anymore yeah now technically react query does have a refetch function on it that we could use back in i'm back in index so instead of the old fetch function we were exporting before react query has a refetch okay and this would totally work but what this means now is that everywhere we use our posts query like in a component we have to remember to refetch it after something like that happens okay uh i don't like that i don't like having the responsibility to remember when to do things in a lot of different places i'm noticing a pattern in the way you write code yeah let's take this let's let's take this out for a second so because really we're not going to need it and honestly this doesn't have to do that now and actually this is just a an identity function so instead of on submit we could just do create post right here instead of on submit so okay let's go into our use create post hook and let's just take a look at it okay so here's use create post it's very similar to what was happening in the other one right ranging state instead of firing off something automatically we're just kind of returning the mutate function that we can use to pass it some values and just have it go save it on the server right and mutate so when we use this if we look at the index um we are importing this is just an alias for the mutate function yeah exactly excellent okay kind of following that trend from from react yes which is nice like it's i do really like this because then you can use multiples and they would all get unique names as opposed to having to do the i don't like the destructuring alias thing because it it's hard to read um so it's nice to be able to return an array where you just get to assign a name instead um some of that might change in the future uh and i know it's actually gonna change a little bit in the future just because we're moving react query into like an agnostic kind of a position oh nice framework agnostic but for for now it's it's a really nice api yeah yeah and and this so this is really slick so if i want to refactor this then theoretically speaking i'm going to import this is what i was waiting for i just want you to i have a theory that is use mutation instead of use query yeah and this is how api should work it's like oh you learn one part of the api i bet i could do the same thing with this other part of the api i bet it works the same way so for this one um it's going to be create post so it's going to have a unique identifier i assume actually doesn't need one okay so no no unique identifier no the first thing you're going to pass is the function that's going to perform the mutation got it okay so then we will take this part and pass that in so let's break this onto a couple lines and then that's probably it isn't it to start yeah uh all of that other code can can die get on out of here okay so that's gone and uh one thing not to miss here is that mutations usually take variables so you're gonna want to to uh add that variable right that's right that's right okay yeah so so this would be um just an object right yeah the post that we want to create yep cool so we've got use create post and here nothing really changed because use use mutation i'm assuming is returning the same signature so we get our mutation it is and then we shoot it there again yeah right which is great i mean i i think that's a good teaching tool right rather than having to explain why it was one way and not the other um and so this this create post info that's gonna be like the response like success or the post that was created or whatever your your decision is from your api yeah it's it's got all the state so it has the same kind of uh is loading is idle is success error stuff and it has data and error all that good stuff and yeah we're using it down there in that button to kind of hint to our user what's going on yeah excellent so this is great and then once we get so then theoretically speaking if i want to reload save oh did we not delete that part uh maybe i didn't hit save or was it use posts um it was in the admin index actually oh oh oh okay and that's gone now right it should be maybe we just needed to do a hard reload but i don't think that code is anywhere now okay so i'm reloading save what's trying to screams admin oh admin post it's trying to call in here we're on an individual post yep okay so i'm just gonna delete that yeah there you go okay now now we can have the clinactic reveal there we go okay so that's saved and then when i come back out so change go back and let's do that again uh you'll want to look at the title because uh when you when you do that because the form the form that you're editing is just kind of like a draft of of what you're editing so edit edit the title again and hit save but scroll up to the top of the page so we can see the title oh yeah so it didn't change it didn't change and this is where that fetch needed to happen right right but like but like i was telling you you shouldn't have to remember to do that everywhere um you should only have to remember to do that one place and and that should be that use create post mutation that we made right and so if i go in here then i'm going to assume that i've got the ability to pass some kind of like a cache updater bingo yes okay so on here the callbacks for use query and user mutation are very very similar okay it's all just on success on whatever so in in this case it would just be on success and it's just a function you want to run okay and this is where like this is kind of the first official entrance of the react query the query cache into your code so where we want to uh refetch that that post query we need a way to communicate from this mutation over to that that individual post query that it needs to refetch and that's where the query keys come in handy so we use the invalidate queries function the method off of the query cache and you just have to match up the key so posts and post id and now how are we how are we getting the where does the post study come from well right the post id can come from two places uh the data coming back if your api comes back with the data right uh then you could use it there um and but this is a create post you know or no sorry this isn't create post is this you save post this is we have been editing the wrong ones you know that oh we have haven't we yeah let's let's go over to use save post and let's just bring our work with us okay so uh it's it's this uh axios patch right here not then it's this stuff that we kind of want to save so let's let's bring that up here so i'll copy go into save post drop this and then we'll take this part yeah and let's just replace that's we we were just mismatched on yep the story of my life editing the wrong code um okay perfect okay so so this is use save post this is what we've been doing right and it would have worked the same way anyway all the demo we have done is still worth it yes okay but this is where again we could either use the data coming back from this patch which is very standard like people returning the data that you get from the patch right right um or you know so there's error or sorry there's data and then there's also the variables that were sent with uh with the mutation so we could do it from the data or the variables uh i like in this case it is sending it back so we'll just use the actual data dot id cool so this is actually the post that we're getting back nice okay all right this all makes sense to me um so then we can save this uh and then this one this is going to be the same thing right like it would be post and then post.i or there would be no cache to invalidate yeah there there isn't really there isn't an individual post to invalidate but uh when you add a new post you're gonna want that whole thing right yeah and that's where you would just do this you could either do yeah the array version or the string version it's technically the same thing but sneaky okay just like that um perfect so we're not gonna get a we won't need that so we can leave that okay let's give this a shot so um now it's updated let's go with a sixth time i'm going to save uh-oh query cache is not defined oh i forgot to import it so that's fine we're going to use save post and then i'm going to import query cache yeah all right then let's try this one more time but these are good errors you know it's like oh yeah i know exactly how to solve that i i like it because it uh it kind of there we go all right so now it's updating properly and if i go back out yeah we know it's there yep slick okay and end in the admin too so if you're in the admin and it's the same exact data so yeah and i haven't had that i haven't had to like refresh the page this is all try the uh create new post see if that works okay so i'm gonna let me turn this off uh let's create look at it immediately immediately available and notice i just clicked through so i went admin into the editor then i went to view and i never had to reload like it all just worked it was all there that's it it can almost be jarring as a developer if you're used to your application having loadings everywhere when you get into react query and they disappear it's kind of like a whoa uh that was fast was that supposed to work that fast almost feels like you're doing something wrong but it's all amazing yeah so so there's a question in the chat about um using like the dot then so when we so we write this here right like where we've got axios and then we got here so why wouldn't we do a re like a a revalidation here like uh you could you know the these on success uh these life cycles here they're just there as a convenience you you could write all of uh you could write all of your asynchronous logic in the function if you if you wanted so there's a reason we could just like bring this right up into here and it would be okay yeah there's a reason though that people easily get asynchronous flow wrong a lot of the time even i do and these callbacks while they feel kind of like what why you know why do i need a callback to do async logic right and some of it is you get to opt in only to specific parts of it without having to write all of the flow yeah and jordan in the chat just mentioned like if you use dot then you also have to write a catch you also have to handle error states and make sure the data came back so that we could we could easily opt into an on error here you know uh we could do something like unsettled which is just like hey give me give me the post or the error you know whichever one it happened to be you can handle it all in one function and in fact there's also another life cycle that happens with mutations called on mutate which lets us do some really tricky cool stuff if you're interested interesting yeah so i mean we've got we've got 30 minutes on the clock so we can dive into whatever you want here okay so before we move forward with this let's let's add kind of like a quality of life improvement to our application okay um let's go to the index page index and it doesn't really matter where we put it so let's just put it up here by sidebar i guess all right we'll do it all right we'll do it above sidebar okay um and i just have a component here that i've called a global loader i think it's called global loader it's in the components yeah it's in the components directory so you can just import global it's the default export default got it yeah so import global loader from components global loader okay and then let's head back to our application make sure that that's showing up it should just d by default show up in the top uh the top right hand corner of our app here yeah yep right up there um and now let's go into global loader just to check out the code what's going in there okay i'm actually going to change it to fixed okay um but yeah so we're like so so to preface this a little bit and in fact i probably should have showed you this first let's go to just the blog index page blog index yep here yep now you we know now that react query is automatically fetching in the background you know all the time you know and it'd be nice to know when that's happening so you can actually use the uh post query dot is fetching which is different from is loading like is loading is kind of one of those enum states you know it's a hard loading state that doesn't have any data is fetching could be happening at any time during the successful stage of your application right and that's the back that's the background right it's like i'm checking to see if anything that is fetching yeah and and you could show kind of you know hey let's show some loading when that's happening right so any now if you go back to the blog page anytime that you like refocus the window or anything like that it kind of shows you when it's loading um and you can do this kind of like if you're doing a small little app or something like that you can do that in every single place right but what i've seen is it's kind of nice to just have a background loading indicator overall um instead of having to do this like in every single template that you own yeah so instead of doing that one in the blog and every single other page let's go back to this global loader that we were in and we can actually import uh use is fetching okay from react query [Music] really simple hook to use it's just const is fetching equals uses fetching and then just say is fetching then we'll show it otherwise we won't okay and let's do this [Music] yeah just to make just okay and so now what should happen here so save that is yeah we see that kind of come in and out so i'll click come back up there it is and note like it's happening for the blog now it's happening for the admin section now it's happening for the like page edit um yeah that's slick that's really yeah and and it's kind of a fuzzy feeling that it's like oh it we're synchronizing right we're synchronizing with the server um and that global order is going to help us kind of see give us some visual indications about what we're about to do with the mutation stuff yeah that's really nice um and so i see a request in the chat to do optimistic updates is that where we're headed next yes that is exactly where we're going all right let's do it okay so optimistic updates can depend on it can a lot of it can depend on what your api can do and it also depends kind of on your confidence level of your api and your data and what's happening so i'm going to kind of show you on the spectrum of easy to more difficult slash risky uh how you can kind of balance that okay i think we should go over with say use save post let's do the post saving first okay i think this is a good place to do it so optimistic updates there's kind of two stages you can like the slowest way to synchronize is to invalidate queries right because it that means that your mutation went and it came back and then you validate and it goes out and comes back so it's kind of like two round trips right right if you can cut that down to one round trip that's a lot better so if you know for a fact that this post coming back from your patch is the exact post that you would get from your query invalidation yeah then instead of doing the invalidate queries you could just do set query data posts post id post okay so now right so now we don't even need to invalidate uh that that post you know we will still probably want to invalidate all of our posts though so hold up so when i do this okay so that didn't um i think i did that wrong because it looked like it's still loaded um i think it's because we're i'm invalidating queries posts let's take this off for a second now um okay let's see let's see if it works so it happens pretty quick here did i miss a step uh we shouldn't have invalidate queries going off anywhere we're on use save post right use save post oh there we go that seemed to work it did work so how long should it take like it it i guess that's the length of time to run the mutation i was expecting so that's that's my fault yeah so as soon as the mutation comes back that title should update god you should not see a loading up in the top and so so this this is what will happen um this button is going to show us how long the mutation takes to run and as soon as the mutation finishes this should update whereas before the mutation would finish and then this would have to send off another query to refresh the latest version so we can double the load so okay so when i when i do this edited watch this button down here it's going to say saving and as soon as it says saved this one's updated so that's our that's our immediate update yep and we just went down one round trip yeah this is this is safe because we know like this will not happen unless that mutation succeeded exactly and as long as you know that that post coming back is the same post that will get you know refetched on this query right and so so this is less of optimistic updating and more of like cutting out extra work yeah i mean it is kind of a it's not an optimistic update but it's just kind of utilizing what you have in front of you right right right now another level of this is if you were to uh if you were to set this query data here it's instant here but then if you go back to the main blog list that post title will still be out of date in the posts list because they're separate they're separate queries and that's where you can choose like okay they're not on that page so you could just invalidate right but if you really wanted to you could even do it for the main posts query too and you would just kind of like loop through and find the post and replace its data well it takes an updater syntax just like react so okay just like react state does so you can say you know return old map and and you say you know if if d dot id equals the post id then we're going to return the new post otherwise we're just going to return what was there right and now you don't even need this invalidate queries okay because because now we're optimistically updating both the individual query and not optimistically updating we're we're short circuiting right right right right so let's save yeah it says updated go to blog it's updated and we never saw the loader exactly so that's slick that's really nice and and that's not even getting you know optimistic it's that's just kind of normal right that's just like i guess the the efficient way to do this right it's less work to get the desired result it's less work on your on your bandwidth right right and that's just up to you i think a lot of times people over obsess about the bandwidth because if your user refreshes the page they got to make those requests you know and if you can't if your application cannot handle your user refreshing the page every five or ten seconds i think you have bigger problems um on your api side right but it's true that we can be very efficient by by doing this and save on bandwidth where possible with your bandwidth devices and stuff like that so yeah and and like for simple things like this where it's a single list this makes sense i think if you had posts and then you also had a query for comments and you also had a query for likes or something you're probably doing more work to to like manage those caches together than to just invalidate the queries right exactly um so now now we can actually get into like an optimistic update like what is an optimistic update an optimistic update is where you are taking action on the user interface before you actually know if it's been persisted to disk yes and so and this is something that i never really heard about this until graphql started to kind of take the world by storm right like optimistic updates were something that i think were they probably existed before but they were popularized by by graphql absolutely so with using mutation it's pretty easy to do an optimistic update uh we can use the on mutate lifecycle callback and this just says like it's basically when we call the mutation function that's going to trigger this this is going to run first and you get the variables they get called with it so this will be like the new post right and this is the same these are the same yep exactly these are the same okay just for consistency let's change let's update uh these to new post and then the this one too would be new new post or yep yeah we could do that one too so values i'll use new id uh whoops new post there we go okay so it's all it's all the same uh so with this on mutate what we're going to do is we need to um first we're going i'm just going to show you how to just update update the data right and it's it's actually really easy because it's just this it's all the same stuff right here you know so we probably don't need to optimistically update the main post list because it's not on screen sure but this one we can optimistically update the individual post id for sure okay so and then do we need to do this one again or can we leave that out um we well there is a chance that the payload you send is different than the payload you get back because if if we were doing like a use create post or there's validations right we'd get auto generated ids we would get exactly time it's always a good idea to if you're doing an optimistic update you either want to utilize what you're getting back or at the very least invalidate afterwards gotcha okay so if we if we run this let's just save this and just run it as is and let's just see what happens so i'm going to go in here i'm going to grab this one and we'll say edited and save oh we got an error what was our error post is not defined we i think we edited things oh new post there we go tag team all right back again no no one chat huh okay um all right so let's edited i was lagging a little bit on the video there look at it go beautiful oh we still got sarah though what was our error not read property map of undefined did we map in here would it be this okay so yeah it is it is possible here where um if we loaded straight into the individual post then we'd never made a query exactly so um we you could just do you know like if not old like if there's nothing in there then you could just return old which would be undefined right um you can also do this where you can say like if query cache dot get query data posts you know if we have if we have query data for posts then then we'll perform this operation okay now that and that's this okay so this is logical right this makes sense to me where we're saying like if we've queried for the post this one we know we have because we are well actually we're we're just replacing this data wholesale right so there's no kind of updater failure there yeah um the other way we could do it as well is you could just default old to an array if you wanted you should be like hey if it doesn't exist then let's just make it an array uh oh yeah because then and that would also help with um well and you could just say new posts yeah but really the efficient way to do that would be the if right so it's like yeah if it has data then we're going to do that otherwise we're going to basically just set query to post to like you could just include it to have your new post no this will work this will work yes so yeah um yeah so okay so in in this to to just kind of like talk through this code and make sure i understand it what we're saying here is if we have an existing post cache we've hit the home page we've made that query then we're going to replace the the old query of this post id with the changes we just made otherwise if we haven't hit post yet we're going to create a whole new entry for posts which is an array that has only the post we've created yep okay and honestly that that scares me this is all just risk management right this scares me a little bit because i i wouldn't want it to stay that way so i would also do like an invalidate queries here uh on that main post just you know we're setting it optimistically so that it looks good when they go back to that post page right but i don't want it to stay that way and luckily when you invalidate a query that's not being used on the screen right now it doesn't re-fetch it automatically it would create a ton of bandwidth in an application so it just kind of marks it as stale so that the next time it gets kind of shown on the screen it re-fetches in the background got it okay great so then we can we can kind of test this uh do we need to make these changes in create post as well um yeah but they'll be slightly different okay uh let's see so if yeah i mean we'll just we'll just make this happen um let's and we're also only halfway done with the unmute with the optimistic mutation to the optimus ah there's some things my bucket what what what are y'all doing in there what's going on in chat right now um okay so okay so we've got uh we've got um so the okay so chat i see you're playing with sound effects right now to to avoid a lot of noise um all of the the exclamation point redeem those are those are gone um so it's only the the other ones uh which if you want to see them you got to look at the learn with jason repo because i haven't had a chance to like fix this up um they should all they should all be there though they're in the learn with jason uh github repo under functions um but uh but yeah so all right so if we update i save and now there's no error and we can see that things are changed so i go to view post and it's already updated and we never saw that i actually probably did see that i just wasn't looking for it um because we we refreshed uh thank you for the sub pepsi i'm sorry i'm not i'm getting that wrong um so i say we hurry and do the uh like the use create post stuff too yeah let's do it um so over here it's gonna kind of be the same thing i'm just going to kind of paste over what we have but instead of the individual post stuff it's just going to be so use create post well no it is going to be the individual post but i guess it's just a little different right so you'll you'll get the new post back right and um it's it's going to be this right here where we don't have the individual posts to look at but we do have this so we can say okay if if we have the main post query on our page we're going to do the same thing except for we're not going to map over it like really we're just going to add it on so it'll be old so just kind of spread right spread the old ones in and then add the new post right and and this is interesting because this is somewhat optimistic as well kind of risky we don't really know if that's the position that post needs to take in that array you know and and so we're going to want to invalidate that regardless right well and that that seems like something like this is going to matter based on your app right like if if you know for sure every time somebody posts like it's a chat box or a comment then you can optimistically put it at the bottom and you're safe if it's something where you're doing like calendar entries that clearly is not going to be the case so you you would need to run some logic there yeah you'd have to and it's sad that you'd have to duplicate some of the logic from the back end to know exactly where that goes right or hopefully it doesn't matter where it comes back in your query cache and you're like doing it and you don't worry your ui is smart enough to make that work yeah so that should work for our use create post and it's not even optimistic yet okay so from here we could again do the on mutate uh and get the new post and then do um so it's kind of the it's kind of the same deal you know it's like hey if if that's there then we want to add it but we don't necessarily want to invalidate it you know okay yeah and and at this point it's like okay if we're doing it in on mutate do we really need to do it here you know like if if it succeeds then really we just want to and uh we just want to replace it you know i would just say after optimistic updates it's just so much safer to just invalidate sure yeah um that and that's especially when we're working with especially when we're working with arrays and positions and removing items and buckets it gets really complex you need to be able to fall back on something that's pretty safe yeah yeah and and what i like about this too is is that basically what we're doing here is we're saying we're reasonably confident that our mutation is going to succeed right assume and we would want error handling here as well um but you know we'll say like we're reasonably confident that our mutation is going to succeed so as soon as we submit our form we should rewrite the list then as soon as we get a confirmation back we should just in the background not blocking the user experience but in the background replace our query with whatever's true in the database just synchronize yep yeah that's uh you bring up a good point about error handling i think we should jump back over to the use save post and take care of some of that okay um do you want to try this first before we yeah oh yeah yeah let's let's make sure that our ui is still working okay so we've got our here's our list and i'm gonna just a new post and and what should happen is when i save this we should see it immediately show up in the list here so let me yeah oh no post query data map what did we get wrong here um oh i have a theory it was use create post and we said old new post oh oh we're turning it here there we go okay so now try that one more time um look at that that is beautiful um and this is like this is a really nice experience where now holy moly we never just work and we triggered a stampede excellent okay so but this is great like we we just put a flow in place where we use the form as soon as the form submits we're able to put that into memory and we're able to continue using our app as if everything is fine now this is also the workflow though that makes apps infuriating if it's done wrong like if i'm in notion and i type in a bunch of code or i type in a bunch of text and then notion does like a refresh and half of the last sentence i wrote disappears i'm furious so it is really important to handle this well exactly it all comes down to the confidence that you have with your api um so on that on that create post the use create post hook we also need to handle the situation that something goes wrong yes so let me get back into use create post yep and then using some deductive reasoning here i'm assuming we've got on error and if we get something back it will give us an error exactly okay so what happens here though is that by the time the error happens uh there could have already been some stuff uh that happened with uh like optimistic updates right already optimistic up optimistically updated it here so how do you know what to set it back to well you need to kind of snapshot uh so it'd be like the old it'd be the old old no i like it yeah so the old post query cache dot get query data and you just grab the posts okay um so it's just kind of snapshotting like hey what's there right now right okay and then you kind of do your optimistic update and really it's whatever you return right here there's two ways to do this you can return the old post right here and it's going to pop up right here so error variables which are what you called it with so it'd be like the new post and then this is the rollback value so this old post comes in right here this okay so this is our rollback value and that gets dropped here so then if this goes wrong we could do a query cache set query data uh posts to old posts yep and this is our this is our recovery um so we could do something really when something errors it's kind of nice to just invalidate too okay so we could console.log like error just so that we get a like some explanation of what went wrong and then you would also do uh query cache invalidate queries posts yep and see we we've got that two places here now so what we could do here is just say uh unsettled so no matter what we will always you're in my mind so just always invalidate and then the other thing too that's cool here is you don't even have to do the rollback inside of your on error uh you could do something like this where it's just the rollback value is a function so you say like if rollback then call rollback and then up here you just return a function oh and and now we've encapsulated this logic because this like this part makes sense to me right where we we're basically saying like this is what it was this is what we wanted to be and this is our error handler and this this feels very reactive this is kind of like the use effect uh where you've got like a setup and a tear down um and you know that yeah so this this is cool i like this and and and we showed like it'll work both ways right like where we have we we can return just the values and then write the logic in on error or we can we can provide the error handler as like a function of our our optimistic updating logic if we want to group that and so that way if you want to reuse like error handling across your application you can if you want to reuse mutation like on mutation optimistic update logic across your app you can you can kind of compose where you see fit oh that's slick that's really really nice um and the timing here is great because we so we got this we got this running we got our basic air handling in place we've got um i mean we've got a full full app like this this great app that the first time we load the page it loads great uh and then after that we are able to just pop in and out of all this stuff and we get these really fast loads um we can keep testing we update we just keep going new new new it live reloads ah it's beautiful it's uh that is a really really nice pattern this feels good like it feels nice to use this so make sure you go and try out react query uh and like go check out the rest of the tan stack there's so much good stuff in here oh i forgot one more thing that would be cool we we don't have time for it but the next step for this would be pre-fetching so that kind of segues into the future of react where you've got suspense and concurrent mode and stuff sure uh getting into prefetching is really fun like you could do it where if you hover over one of those blog posts we you just prefetch the data so that by the time you click on it it's there nice okay yeah so um and then tony just shared a a discord link um so i will pull that up as well we'll make sure that makes it into the um into the show notes is there anywhere else that people should go if they want to follow up with you or learn more about this uh no twitter the discord is actually a great place lots of people there talking about react query and answering questions the community is very helpful very hot right now so excellent great place to learn cool um so tanner thank you so much for for taking the time to teach us today this is a really powerful tool i can see huge potential for this and i i can definitely see this showing up in a lot of projects from here chat uh as always thank you for hanging out with us and make sure that you check out our sponsors we um we have live captioning happening today brought to you by white co-captioning so thank you as always uh and that is made possible by netlify fauna sanity and off zero who all chipped in to make the show more accessible to more people which i really appreciate from the bottom of my heart um with that i think we're going to call this episode a success tanner thank you so so much chat stay tuned we're gonna raid and make sure you check the schedule because we got some good stuff coming up on thursday wait wait wait wait look at it uh we have zel coming on to teach us how to build a no framework javascript drag and drop so this is gonna be a good deep dive into how javascript actually works and and how you can do things using just the platform no no frameworks or tool sets um so make sure you tune in on thursday make sure you check the schedule for the rest of what's coming on because we've got a lot of really exciting stuff coming up i i can't i cannot even start with how excited i am for for this whole schedule um i think that's the show tanner thanks again we'll see you next time you
Info
Channel: Jason Lengstorf
Views: 31,042
Rating: 4.9680367 out of 5
Keywords: Learn with Jason, Tanner Linsley, Nozzle, React Query, React
Id: DocXo3gqGdI
Channel Id: undefined
Length: 89min 3sec (5343 seconds)
Published: Wed Oct 21 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.