React Query tips from the maintainer @tkDodo

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
today I have with me Dominic one of the lead maintainers of react query all the way from Austria Dominic thanks so much for spending some time with us here today hi thanks for having me in my previous video we looked at some ways to do some refactoring of react hooks mainly around the use of react query it looks like there's probably some better ways to do some of the things we did so we're going to start here with the solution that I came up with in my last video and let's talk through some of the things that we could have done better my goal here was to kind of like abstract some of the complexity of a bunch of underlying hooks into something that I could use as a single hook in a react component but what are you seeing here Dominic is kind of some of the red flags that are standing out to you what I usually like to do with these things is kind of just see it in the browser see how it works like start from the user perspective what we actually want to achieve because a lot of the time when we look at the code and we start to see all the abstractions and especially when user factors evolved I always get a little bit like it's not a red flag because there are uses for it but a lot of the time I kind of get my the senses like up thinking like is there a way to get rid of it because a lot of the time there is and by getting of the use effect we get rid of a lot of the complexity that it brings with it and the things that make it a little bit hard to understand and often it also use effect comes especially if you combined with use state it comes with additional render cycles that are probably not necessary so maybe what we can do is take a look at the code that I started with and we can see is there a way to improve some of that this is kind of the code I started with at the beginning of my last video it feels like there's a lot going on here the fact that I've got like four different hooks that I'm calling because I can't put anything here in a conditional I'm checking in multiple places to see if I'm dealing with the widget hooks or the gadget hooks what do you think is maybe a good place to start with simplifying some of this what stood out to me in the other solution was that we weren't actually calling use Query anymore that's a really important thing to do because use Query is the hook that's going to create the subscription for your data that lives in the query cache so without calling use Query when we just call use Query client and then we get the data from it in an imperative way our components will not re-render when new updates come in because react query tries to keep our screen up to date this is only possible with the use Query hook so that's why I kind of like this solution a little bit better subscribing to both of these things doing what we are query is good at so let me quickly take a look into the use get widgets and the use get gadgets work so we are this is basically just a standard use Query that is fetching both of them okay so can we maybe with this can we start with the browser and see the application how it runs what is what it's what it's doing yes so this should be running the first version of this code we have this widget and this gadget here exist in the local cache these two do not and so if we we can see them this updating here if we go to widget five we see we have the post Gadget five we see we have the post as well okay well then let's maybe start with the mutation itself because what I'm what I'm missing from the whole example was tying the mutations and the queries together I think this is missing in uh in both Solutions because what we are doing with the mutation is firing it up and then when we get the result back we are setting it into local state saying oh yeah now you've you've selected the gadget 5 for example I often feel that mutation the the data that mutations get or that that use mutation actually does they're kind of ephemeral it's also the reason why use mutation doesn't share State around like use Query does use mutation is basically just a small wrapper around making a request on demand and giving you back some ways to handle the loading in the arrow States and then obviously all the callbacks that are very handy to use they're not really meant for taking the data and then working with it like putting the data into local state or somewhere else directly because what happens is that once you get a refetch of your query this data will not be in there so if I have widget one and Gadget 3 in my query cache and then I make a mutation that adds the widget five on the server it will be added so in the database I now have three things so at that point what I want to do is I want to update my query cache in order to you know have the correct State reflected in there rather than take what the mutation gives me and store that in state and that's where we're kind of tying the tying the loop together that the mutation is just there to make the actual update but it's not our state manager that is what what use Query is and there are basically two ways to to make that happen one way is that we can just take the data that we get back from the mutation and then write it back to the query cache in the correct position or like if it's a list we have to append it for example if you know it was an ad mutation and so on that is the the way that has the most amount of code but it doesn't make an extra request the simpler way is to just refetch the query so you would just tell your query hey I've added something to a list of things to a resource now please invalidate my cache and get me the latest resource and after that you have those things in the query cache which means it doesn't matter that from the perspective of your application only the first two things are in the query cache and the others are not what we're doing is we're always reading it from the query cache we're just trying to make sure that when we make the mutation we actually do it and then we make sure that the cache is up to date yeah that makes sense for this to actually work what I think I might want to do here is to kind of Mark the idea of our database if we just kind of create a array here and then we can just move these these items up to here we have our hook set up to make it easy to like modify that state we have our server State being updated accordingly so why don't we look at the way that we would just refetch trigger a refetch on on our queries so the way this is working right now we're actually like using the data that comes back from our mutations but instead we just want to re-trigger our queries here so um we can kind of drop this whole user fact right I guess that's the uh that's the point to not put it into state but to only kind of read it from the from the query cache yeah so let's drop our use effect here like let's say the first time we look at this you know this data is not in our local cache and then we want to actually trigger our mutations what do you have any patterns that you like for kind of using the mutation and then triggering these queries to happen yeah so the the standard approach to this is to use one of the callbacks on use mutation so use mutation has the on success on error and unsettled callbacks and usually we could go for unsettled which means whatever happens you know even if there's an error get the latest data from the back end because we have done something so we know something in the back end has changed we can just do this on the custom hook and use mutation because use mutation takes another options argument as second uh parameter exactly and there we can call the unsettled callback we call query client dot invalidate queries and that is the main method of how I would like to interact with the query cache which is just telling it which piece of data is now no longer valid the invited queries method will then know automatically which queries it actually has to refetch depending on which queries are currently being used so for the use post widgets we can invalidate our widgets for the gadgets we can invalidate the gadgets and then we kind of have it have this full circle cool and so we're just using the query key here and invalidating based on that query key this query client reference that I have here is actually just the imported client we're not using the use Query client Hook is there a Best practice around like using the hook versus just importing that instance just directly yeah so I usually always say please use the use Query client hook and I think there's three reasons for it when you're using server-side rendering you're not creating the query client outside of your application you're creating it inside the app component so that data isn't shared on the server between users and then you can't import it because you can't export it it's just like part of your of your application and also you have an easier time setting up for testing because if you have a query client that has different options for production and testing by just writing your code to always call use Query client it will always just read whatever is in the react component tree what is in the nearest provider so that means you can in your test wrap it in a provider that contains a test query client while in your app you're using the real one whereas if you just import it your code is actually fully tied applied to the production query client that you have and you have no chance of stabbing that out basically and then you go you go to like js.mok or whatever and then it kind of gets ugly and yeah well absolutely awesome all right so we've got our mutation hook set up to properly invalidate the right queries so we're calling this hook if we look back at our app TSX file we want to be able to update this state here right our data now we don't have this product anymore right because we got rid of that local state so it's the best thing to do just to find it in our lists here widgets.find or gadgets.find is that the is that the right approach here I think I think we are going to do that but not from inside the mutated function but inside the render function and then we'll find out that we're missing one thing but it's good because we're kind of like we're going to see what we have to reintroduce so if you're trying to do widgets.find we'll see that we don't have the ID and that brings us to like what is our real minimal state representation that is what the user has actually selected so we need to introduce some State back but it's not going to be the full product it's just going to be the selected ID so we are going to we're going to need some some form of state and this is really one of the best advices hopefully I can give is that to really try to find out what is the most minimal state that you can have and store that in state and try to derive as much as possible from everything else that you have available so that minimal state is the minimal State based on like the user's interaction with your application and from that you can derive data like out of the cache that is within react query is that the idea yeah the minimal state is the minimal thing that you need to show your interface correctly and everything that you can compute you do compute because if you take the full product and copy it into State you will see that you have two versions of it you have one version that lives in the use State and you still have another version of the product living in the array of widgets in your query cache and then you have two versions of things either they get out of sync sometime like when we are query as an automatic refetch or you try to set up State syncing like a use effect if this changes I copy it back to my local zip and then you're kind of creating all all this complexity that is unnecessary if you just see the idea is the only thing I need and I can compute which I want gotcha so there's a case here where we don't actually have any widgets at all so I guess first we need a guard for that we also have the case where we don't have a selected ID right so what yes um instead of those guards we could also just do widgets question mark dot find and then we just let it find the null ID and this will just give us back undefined if there's nothing right so that's that's also good perfect and we can do the same thing here for gadgets that I guess replaces yeah one of those uh and then I think we can probably like just drop all of these conditions in here we're still not setting the ID right into into our selected state so I think that's that that's what's missing in the this mutate function that that we have so that happens on the button click and then we need to basically find out if we should run the mutation at all right because if it's already in the cache we don't need to and this is basically the same thing that we have in line 76 and 77 we have like this find that tries to find them so I would probably extract that to a function get product or find product or whatever then we can call that in the function as well just to determine like do we have it already then we don't need to do anything we just set the ID react we render the component for us and it will then find it again and return it if not we have set something into state that doesn't exist yet our product will still be undefined but then we'll fire off the mutation and after that eventually you know it will re-render with the new widgets that or gadgets that come back from the mutation and then it will find it and pick it up gotcha okay now we can replace that with fine product so we can replace data here with product and then after the set selected ID we will probably just call find product again with the new ID if we don't have it then we fire the mutation there is this great video from David kurushnet where he says you know most of your side effects and interactions should actually happen in event handlers and not in effect and I think this is a very good example because here we already have the event handler and the side effect where we can easily know should be fired a mutation or not and if we go to bind it to the render cycle we would for example get into a situation with react 18 and strict effects where this would just fire again this is something that cannot happen if you just keep it in the event handler yeah I feel like this approach definitely makes a lot more sense to me when the user performs an action that's when we do the work we don't queue up a side effect to happen afterwards so back here in the browser so if we refresh this our widget cache here only has those three items in it and then if we go over which of five here now we can see we've got four items and then if we go to gadgets here we can see the cache only has those three items in it and then we can add our fourth one to it and if we if we take another look at the hook I think when we look at the code it's like I think it shrunk a little bit right so there's like a bit of difference it has gone away and I think the duplication that you have now with widgets and gadgets it's not that bad right so it's true yeah we've gotten rid of many of the conditions that were in the original logic I think by moving some of those conditions some into like selector functions like find product here and then I'll also by using unsettled to manage the updating of the state instead of doing it ourselves now we kind of have a much clearer pattern to read here yeah I like that I wouldn't change that much from here on actually one of the things I I heard a lot from people watching the the last video was that they would make this two separate hooks one for widgets and one for gadgets my take on that is that in our application code here or in our component I kind of want to have a single Handler here that I can use for all of my buttons regardless of what that input from the user might be and if I make those two separate hooks I'm moving the complexity I guess out of this one hook but I'm moving it up into my component what's your take on that I don't think that it would turn out too differently actually the part that's more important was to get rid of the uh copying the product into the state and get rid of the use effect and if you look at this now I don't think it matters much right because it's not that much code that you have and you you will always have a little bit of duplication with those two things the only other thing that we could do is to actually make it one query I think it's at least interesting to think about because when we think about react query and queries we often think that it has to be one request in the query function if I want to make one request I make one query key and one resource with one request but it doesn't have to be that way if we know that we want to actually work with products and it doesn't matter that much that we have widgets and gadgets then what we could do is we could have one use product hook that has one use Query in it and the query function could actually make two requests and we would just get the things back and return both of them and this has advantages and disadvantages right in your solution it might have a bit of Advantage because you'd kind of want to treat them as the same the disadvantage obviously is that those things are then bound together and they will always live together and they will update together and so it means that if you create a widget you will probably refetge widgets and gadgets if we invalidate them even though it's two requests they are treated as one resource this is kind of the trade-off to the situation but it's something that can come in handy if that's what you want if you only have separate requests because that's how the API is built there is no need to separate them into much multiple Hooks and I think that's a good thing to know and a good tool to have in your tool belt that's really cool for this application we can say that like elsewhere in the app we probably do use gadgets and widgets separately and so this feels like the right separation well this feels like a really great place to lead this we've made some huge improvements I feel like this hook even though we still do have these four hooks when I originally wrote this and had four different hooks being called in here that felt like the complexity but turns out the complexity was kind of Elsewhere and by managing our state a little bit better using four hooks is not so bad at all Dominic thank you so much for walking us through this where can the people find you absolutely yes so I am available on Twitter most of the time so I go by the name TK dodo online I also have a Blog at TK dota.eu where I write about mostly about react query but also about other things react and typescript can subscribe to it it's a good place to stay up to date also with the developments that we do on react query version 5 which is the new major version that we're working on so whenever I'm deprecating some things on my blog and on Twitter you're the you would be the first to know so if you want to keep up to date that's the place awesome thank you so much thank you bye
Info
Channel: Andrew Burgess
Views: 7,625
Rating: undefined out of 5
Keywords: Programming, Coding, Reactjs, React-query
Id: PtHRZqh3LHI
Channel Id: undefined
Length: 16min 19sec (979 seconds)
Published: Mon May 01 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.