Working with External Libraries 5 exercises

Targeting Overloads with useQuery

Let's start by looking at the nine overloads for the useQuery function.

Each overload has very similar type arguments: TQueryFnData, TError, TData, and TQueryKey:

export declare function useQuery<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey exte

Loading solution


00:00 Okay, let's dive in, see what these overloads are up to. So we can come on click here to see them. And we've got many, many, many of them. Good old nine overloads here. So I'm going to word wrap just to see everything. Now, we start here with a useQuery function. And we can see that all of these overloads, it seems to me, have a very, very similar type arguments on them.

00:19 So if we look here, we can see that there are nine examples of this tQuery function data, tError, tData, and tQueryKey. So all of these have the same type arguments on them. That's a good start because it means that they're consistent. It means that however you call them, they're going to be slightly different. We can see the ones at the bottom

00:38 taking two runtime arguments, or it looks like three. Yeah, three. So this first API that we're seeing here has queryKey, queryFunction, and then options. And the options takes in omit useQueryOptions. Oh, blimey, useQueryOptions.

00:57 And we're passing in all of our type arguments here and we're omitting two things. We're omitting a queryKey. Sorry, I just bumped that down. QueryFunction and initialData. Now, all of those three arguments at the bottom, apart from... Yeah, all of those three overloads at the bottom,

01:16 both taking queryKey and queryFunction and omit them from the options here. And we'll take a look at what initialData is doing in a second. So those are the bottom three. Then these ones here, it takes in queryKey and options omits the options queryKey. So what's the difference here?

01:34 Well, the difference here is that this one in these three overloads, the second three up here, they take in the queryFunction in these options here. So this means you can call it by passing in the queryKey and then passing in the queryFunction in the options. And then this first one up here,

01:51 it just takes in a single argument of the options. So let's just take a look at those just to figure them out. So we have useQuery. The first three overloads are all about just passing in like useQueryFunction and then getUser. And then you can pass in the queryKey here too.

02:09 So the queryKey, I think, is like an array like this, so user. That's the first overload. The second overload... Well, let me just check actually what the course of these overloads are. So the second group of overloads, you can take in the... Where's my word wrap? Yeah, you pass in the queryKey first.

02:28 So we can take this queryKey. And now we are hitting the second overload when we do this. So this is a valid call as well. And the third overload is when we don't do any of those and we instead pass in the queryKey and then the queryFunction and then the options. And so if I pass in the queryFunction here,

02:47 which is getUser, we can then pass in... We then can't pass in queryFunction or queryKey here. So those are the overloads that are going away, as far as I know, is the ones at the end here. But now we need to look at the initial data here.

03:05 And this is a really, really interesting setup because the initial data, as we can see, actually has kind of like three states depending on which overload setup you're in. So in this first three, we can see that we're omitting initial data from these useQueryOptions.

03:23 And we then pass it in as a function which returns undefined. So let's try and hit this overload. Let's see what's going on. So let's go passing in our queryFunction, which is getUser. And we're going to say const result equals this.

03:41 And this result is going to be... Let's pass in initial data, which is a function which returns undefined. Now, if we command click on here, we're going to go to the overload that we're on. And we've managed to hit this overload. And what we can see is that the return value of this is a useQuery result with tdata and tarray.

04:01 Okay, cool. What does that mean? That means that is going to be this or undefined, user or undefined. But now, if we were to pass in the initial data here, we would hit a different overload. So we're actually going to hit this overload instead. And I'll show you.

04:18 So we can actually stick in here a full name and job developer. Now, when we command click on useQuery, we see we don't go to the top overload. We go to the second overload. And we're now hitting this initial data.

04:35 And now, instead of a useQuery result, we hit a defined useQuery result. Let's take a look at them. Defined useQuery result is... Ooh, where'd it go? Defined useBaseQuery results... Ooh, blimey. Defined queryObserverResult.

04:52 QueryObserverSuccessResult is, I think, where we want to look. And you can see that data here is tdata. Whereas if we go back to the useQuery result, we go into useBaseQuery results, we go into queryObserverResult, and we look at tdata here.

05:12 Oh, no, it's down here. We have a loading result here. And the loading result is undefined. So you can see here that if we check for isLoading true, then we're going to see that our data is undefined. But if we look at the defined version, so the defined queryObserverResult,

05:30 actually, we're never in a loading state. We're just in a refetch error or in a success result, which is really cool. So this, we can see, this is absolutely amazing setup, actually. I really love the way they've done this. As we can see, all of the states that are in a normal queryObserverResult, so where you don't have initial data,

05:49 and all of the states where you have data all of the time. So these are the first two overloads here, and it looks like the third overload, if we take a look here, where's the third overload? Yeah, it doesn't omit anything, just lets you parse in all of the useQuery options, and then we get a useQuery result. So we've got then three possible states we're in,

06:08 which is we have initial data, which is defined as something which returns undefined or actually parsed as undefined because we've got an optional thing here, or we've got optional data being parsed either as the query function data or as a function that returns that data.

06:26 And then we've got a mishmash of both because we need kind of this in order to resolve any situations where they could be one or the other. And so it's safe to have a third overload here. Then you can see that all of the other overloads, they just copy that behavior. So the first one of the situation where we're parsing in the query key, it has the same behavior.

06:45 So we've got initial data here and initial data there, and we've got no initial data being omitted from here. And the same situation on this one too, we've got initial data there and initial data on the eighth overload where it's parsing in and inferring tQuery function data.

07:05 So that's so cool, but it does show you kind of how, if you have multiple polymorphic ways to call your API, you get this explosion in function overloads because here we're basically taking three ways to call useQuery. And then we're also timesing that

07:24 by three different situations that initial data could be. It could either be parsed, in which case you get a defined useQuery result, or it could be not parsed. In other words, you get a loading state or it could be kind of like one or the other. In other words, you need a third overload

07:42 just to capture that possibility. So useQuery is extremely complicated as we can see, and knowing how all of this stuff works is not necessary to use it. You can just use it in a normal way, but it might be useful to understand how that works if you want to build an abstraction on top of it.