-
Notifications
You must be signed in to change notification settings - Fork 31
Description
I'm working on a page with 2 dependent queries. One depends on a page parameter and one depends on another query. If I roughly follow the tutorial, I get this:
// Root profile type
const rootProfileTypeQuery = useQuery(
`profile-type:${PUBLIC_ROOT_PROFILE_TYPE_ID}`,
() => getProfileType(PUBLIC_ROOT_PROFILE_TYPE_ID)
)
$: rootProfileType = $rootProfileTypeQuery.data
// Profile
$: id = $page.params["id"]
const profileQuery = useQuery(`profile:${id}`, () => getProfile(id as string), {
enabled: !!id,
})
$: profileQuery.setOptions(`profile:${id}`, () => getProfile(id as string), {
enabled: !!id,
})
$: profile = $profileQuery?.data
// Profile type
const profileTypeQuery = useQuery(
`profile-type:${profile?.profile_type.id}`,
() => getProfileType(profile?.profile_type.id as string),
{ enabled: !!profile }
)
$: profileTypeQuery.setOptions(
`profile-type:${profile?.profile_type.id}`,
() => getProfileType(profile?.profile_type.id as string),
{ enabled: !!profile }
)
$: profileType = $profileTypeQuery?.data
There's an apparent problem: I have to re-type the entire thing every time something changes.
One could just re-create the entire query reactively, but it doesn't feel right. There must be a reason for the enabled
parameter to exist.
// Root profile type
const rootProfileTypeQuery = useQuery(
`profile-type:${PUBLIC_ROOT_PROFILE_TYPE_ID}`,
() => getProfileType(PUBLIC_ROOT_PROFILE_TYPE_ID)
)
$: rootProfileType = $rootProfileTypeQuery.data
// Profile
$: id = $page.params["id"]
$: profileQuery = useQuery(`profile:${id}`, () => getProfile(id as string), {
enabled: !!id,
})
$: profile = $profileQuery?.data
// Profile type
$: profileTypeQuery = useQuery(
`profile-type:${profile?.profile_type.id}`,
() => getProfileType(profile?.profile_type.id as string),
{ enabled: !!profile }
)
$: profileType = $profileTypeQuery?.data
Working with this doesn't feel good and I'm trying to find solutions to tap in SvelteQuery's great features but with an API that's a bit less insane.
What if it was generated on the fly?
When a query is created, we know what we want to send it even if we don't have it yet. Example:
// Profile type
const profileTypeQuery = useQuery(
`profile-type:${profile?.profile_type.id}`,
() => getProfileType(profile?.profile_type.id as string),
{ enabled: !!profile },
)
$: profileTypeQuery.setOptions(
`profile-type:${profile?.profile_type.id}`,
() => getProfileType(profile?.profile_type.id as string),
{ enabled: !!profile },
)
$: profileType = $profileTypeQuery?.data
- I know that the ID of the query has to receive a part of
profile
. - I know that the fetch has to receive a part of
profile
- I know that the query mustn't be enabled unless I have
profile
Why not use generator functions to generate the ID and the fetch only when enabled
is turned on?
// Profile type
const profileTypeQuery = generateUseQuery(
profile,
profile => `profile-type:${profile?.profile_type.id}`,
profile => getProfileType(profile?.profile_type.id),
profile => ({ enabled: !!profile }),
)
$: profileTypeQuery.setInput(profile)
$: profileType = $profileTypeQuery?.data
This isn't necessarily shorter than the other ones, but it's definitely cleaner and it can be put inside a function somewhere and imported in a component.
// api.ts
export function queryProfileType(profile?: Profile) {
return generateUseQuery(
profile,
profile => `profile-type:${profile?.profile_type.id}`,
profile => getProfileType(profile?.profile_type.id),
profile => ({ enabled: !!profile }),
)
}
// +page.svelte
const profileTypeQuery = queryProfileType(profile)
$: profileTypeQuery.setInput(profile)
$: profileType = $profileTypeQuery?.data
And now we can use Svelte Query in 2 lines from a component's perspective. Doing this with the current API requires quite a lot of boilerplate that could be saved by generator functions.
// api.ts
function queryProfileType(id?: string) {
return useQuery(`profile-type:${id}`, () => getProfileType(id as string), { enabled: !!id })
}
function setOptionsProfileType(
query: UseQueryStoreResult<ProfileType, unknown, ProfileType, `profile-type:${string}`>,
id?: string,
) {
return query.setOptions(`profile-type:${id}`, () => getProfileType(id ?? ''), {
enabled: !!profile,
})
}
// Profile type
const profileTypeQuery = queryProfileType(profile?.id)
$: setOptionsProfileType(profileTypeQuery, profile?.profile_type.id)
$: profileType = $profileTypeQuery?.data
One could make generator functions to avoid duplicating code in api.ts
but it's even more boilerplate for something that should be simple.
function queryId(id?: string): `profile-type:${string}` {
return `profile-type:${id}`
}
function queryFetch(id?: string) {
return () => getProfileType(id ?? '')
}
function queryOptions(id?: string) {
return { enabled: !!id }
}
export function queryProfileType(id?: string) {
return useQuery(queryId(id), queryFetch(id), queryOptions(id))
}
export function setQueryProfileType(
query: UseQueryStoreResult<ProfileType, unknown, ProfileType, `profile-type:${string}`>,
id?: string,
) {
return query.setOptions(queryId(id), queryFetch(id), queryOptions(id))
}
// All of this to be able to do
const profileTypeQuery = queryProfileType(profile?.profile_type.id)
$: setQueryProfileType(profileTypeQuery, profile?.profile_type.id)