PhpStorm freezes on code completion when using mergeQueryKeys()
tklaas opened this issue ยท 11 comments
There are quite a few problems you can see in the attached video
using version 1.3.2
Aufzeichnung.2023-08-01.161339.mp4
- code completion poup needs a few seconds to be shown (I hit CTRL+ Space immediately after typin "<sp")
- applying code completion takes an unusable long time (hit Enter when the poup closes)
- syntax errors (red underlines) need a long time to be shown)
I tried a few things to narrow down the source of the lag:
- completely removing all JS Code within the file => no lag
- using key and function directly => no lag (see image below)
- using export from
createQueryKeys
directly => no lag - using export from
mergeQueryKeys
with only teamQueries => extreme lag - returning just a string or array in query function does not change anything
File: index.ts
import { mergeQueryKeys } from '@lukemorales/query-key-factory';
import { teamQueries } from 'src/queries/queries/teams';
export const queries = mergeQueryKeys(teamQueries)
File teams.ts
import { createQueryKeys } from '@lukemorales/query-key-factory';
import TeamMember from 'src/models/TeamMember';
export const teamQueries = createQueryKeys('teams', {
allStats: () => ({
queryKey: [''],
queryFn: () => {
const query = TeamMember.includes([{members: 'person'}]).all()
return query.then(data => data.data);
}
}),
})
I have also submitted a bug report to Jetbrains for the freezing UI, but it sees that the problem is related to query-key-factory or my setup of using this lib
https://youtrack.jetbrains.com/issue/WI-73631/Indexing-on-each-start-of-PhpStorm-and-slow-unusuable-code-completion
https://youtrack.jetbrains.com/issue/WEB-62694/Slow-highlighting-when-use-lukemorales-query-key-factory-lib
@tklaas this is the first report on an issue like this, but I can see mergeQueryKeys
being the cause of the slowness as it is iterating over each array member dynamically to infer the type of each object. May I suggest using createQueryKeyStore
instead and see how it behaves?
@lukemorales
using createQueryKeyStore in my index.ts works and there is no lag or freezing UI.
At leats when I wrote the query keys directly within this file
export const queries = createQueryKeyStore({
teams: {
allStats: () => ({
queryKey: [''],
queryFn: () => {
const query = Team.all()
return query.then(data => data.data);
}
})
}
})
But I cannot separate my queries in different files since I am getting these errors:
in index.ts - "teams" as key got the TS error:
TS2322: Type '{ allStats: () => { queryKey: string[]; queryFn: () => Promise<PersistedSpraypaintRecord[]>; }; }' is not assignable to type 'QueryFactorySchema'. Property 'allStats' is incompatible with index signature. Type '() => { queryKey: string[]; queryFn: () => Promise<PersistedSpraypaintRecord[]>; }' is not assignable to type 'FactoryProperty | DynamicKey'. Type '() => { queryKey: string[]; queryFn: () => Promise<PersistedSpraypaintRecord[]>; }' is not assignable to type 'DynamicKey'. Type '{ queryKey: string[]; queryFn: () => Promise<PersistedSpraypaintRecord[]>; }' is not assignable to type 'KeyTuple | QueryKeyRecord | DynamicQueryFactoryWithContextualQueriesSchema | DynamicQueryFactorySchema | DynamicKeySchemaWithContextualQueries'. Type '{ queryKey: string[]; queryFn: () => Promise<PersistedSpraypaintRecord[]>; }' is not assignable to type 'DynamicQueryFactorySchema'. Type '{ queryKey: string[]; queryFn: () => Promise<PersistedSpraypaintRecord[]>; }' is not assignable to type 'QueryKeyRecord'. Types of property 'queryKey' are incompatible. Type 'string[]' is not assignable to type 'KeyTuple'. Type 'string[]' is not assignable to type '[ValidValue, ...(ValidValue | undefined)[]]'. Source provides no match for required element at position 0 in target.
and in any file where I am using it, there is no code completion:
@tklaas I've created this dummy repro to match what you just shared and I get no typescript errors:
import { createQueryKeyStore } from '@lukemorales/query-key-factory';
type PersistedSpraypaintRecord = {
id: string;
};
const Team = {
all: async () => ({ data: [] as PersistedSpraypaintRecord[] }),
};
export const queries = createQueryKeyStore({
teams: {
allStats: () => ({
queryKey: [''], // BTW, you should use `null` here if you don't need any dynamic values
queryFn: () => {
const query = Team.all();
return query.then((data) => data.data);
},
}),
},
});
queries.teams.allStats(); // works just fine
Can you reproduce your error in codesandbox?
Here is an example what I described in this comment:
#74 (comment)
The slow code completion works by using createQueryKeyStore
, but I won't be able to separate my queries into multiple files as I could with mergeQueryKeys
import { createQueryKeyStore } from '@lukemorales/query-key-factory';
type PersistedSpraypaintRecord = {
id: string;
};
const Team = {
all: async () => ({ data: [] as PersistedSpraypaintRecord[] }),
};
const teamQueries = createQueryKeyStore({
teams: {
allStats: () => ({
queryKey: [null], // TS error for type null
queryFn: () => {
const query = Team.all()
return query.then(data => data.data);
}
})
},
})
teamQueries.teams.allStats(); // this works fine
const teamQueriesObject = {
allStats: () => ({
queryKey: [''],
queryFn: () => {
const query = Team.all()
return query.then(data => data.data);
}
})
}
const queries = createQueryKeyStore({
teams: teamQueriesObject // teams got the TS error below
})
queries.teams.allStats(); // this completion does not work.
TS error for teams:
(I really like the new layout for TS errors from Jetbrains :) )
Oh, ok I think I know what the issues are:
- I completely overlook this in your example, but when you create a queryKey definition with a function
allStats: () =>
, the type-system will understand that you're creating a dynamic key, where the function has arguments and you need to pass these arguments to thequeryKey
array, so for your example the correct way would be:
const teamQueries = createQueryKeyStore({
teams: {
allStats: {
queryKey: null,
queryFn: () => Team.all().then(data => data.data),
}
},
})
- For the second approach:
const teamQueriesObject = {
allStats: {
queryKey: [''],
queryFn: () => Team.all().then(data => data.data),
}
}
const queries = createQueryKeyStore({
teams: teamQueriesObject // teams got the TS error below
})
What's happening is that the type-system needs to infer the array as a tuple ([string] instead of string[]
), and since it can't, it starts complaining. Two ways of fixing that are:
a. For this case, using null
without initializing the definition with a function.
b. For dynamic keys, declare with as const
to make it a tuple: queryKey: [page, limit] as const
const teamQueriesObject = {
allStats: {
queryKey: [''] as const,
queryFn: () => Team.all().then(data => data.data),
}
}
const queries = createQueryKeyStore({
teams: teamQueriesObject // this now works
})
But your issue gave me an ideia of a improvement, perhaps I should deprecate mergeQueryKeys
for being low in performance and expose an util that allows what you're trying to do:
// queries/teams.ts
const teamQueriesObject = createQueryScopeDefinition({ // name TBD
allStats: {
queryKey: [''],
queryFn: () => {
const query = Team.all()
return query.then(data => data.data);
}
}
})
// queries/index.ts
const queries = createQueryKeyStore({
teams: teamQueriesObject
})
Thanks for your quick replies and helping with the typescript errors. ๐
I think I can now refactor my code to use createQueryKeyStore with your provided workaround to use different files for scope separation.
Hi @lukemorales
I am also hitting performance issues in phpstorm with mergeQueryKeys
. Am i right in understanding that we need to wait for the createQueryScopeDefinition
util mentioned in #74 (comment) before we can use createQueryKeyStore
and import from separate files with code completion etc?
Thanks for this great library ๐
I got time to test it today. switching from v1.3.2 to 1.3.4 solved the issue. PhpStorm ist not freezing anymore and reverting to 1.3.2 makes the issue reproducable.
Thanks for fixing this ๐
Is there any advantage using mergeQueryKeys
instead createQueryKeyStore
?
with createQueryKeyStore
I have the advantage that all keys are defined within index.ts so there are no duplicated keys possible
// index.ts
export const queries = mergeQueryKeys(teamQueries)
//teams.ts
export const teamQueries = createQueryKeys('teams', { ... })
vs.
// index.ts
export const queries = createQueryKeyStore({
teams: teamQueries,
})
//teams.ts
export const teamQueries = {...}