-
-
Notifications
You must be signed in to change notification settings - Fork 493
feat(query): enhance React Query integration with per-type query key support #2361
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -726,9 +726,8 @@ const getQueryFnArguments = ({ | |||
}; | ||||
|
||||
const generateQueryImplementation = ({ | ||||
queryOption: { name, queryParam, options, type }, | ||||
queryOption: { name, queryParam, options, type, queryKeyFnName }, | ||||
operationName, | ||||
queryKeyFnName, | ||||
queryProperties, | ||||
queryKeyProperties, | ||||
queryParams, | ||||
|
@@ -759,10 +758,10 @@ const generateQueryImplementation = ({ | |||
options?: object | boolean; | ||||
type: QueryType; | ||||
queryParam?: string; | ||||
queryKeyFnName: string; | ||||
}; | ||||
isRequestOptions: boolean; | ||||
operationName: string; | ||||
queryKeyFnName: string; | ||||
queryProperties: string; | ||||
queryKeyProperties: string; | ||||
params: GetterParams; | ||||
|
@@ -1262,6 +1261,7 @@ const generateQueryHook = async ( | |||
options: query?.options, | ||||
type: QueryType.INFINITE, | ||||
queryParam: query?.useInfiniteQueryParam, | ||||
queryKeyFnName: camel(`get-${operationName}-infinite-query-key`), | ||||
}, | ||||
] | ||||
: []), | ||||
|
@@ -1271,6 +1271,7 @@ const generateQueryHook = async ( | |||
name: operationName, | ||||
options: query?.options, | ||||
type: QueryType.QUERY, | ||||
queryKeyFnName: camel(`get-${operationName}-query-key`), | ||||
}, | ||||
] | ||||
: []), | ||||
|
@@ -1280,6 +1281,7 @@ const generateQueryHook = async ( | |||
name: camel(`${operationName}-suspense`), | ||||
options: query?.options, | ||||
type: QueryType.SUSPENSE_QUERY, | ||||
queryKeyFnName: camel(`get-${operationName}-query-key`), | ||||
}, | ||||
] | ||||
: []), | ||||
|
@@ -1291,12 +1293,12 @@ const generateQueryHook = async ( | |||
options: query?.options, | ||||
type: QueryType.SUSPENSE_INFINITE, | ||||
queryParam: query?.useInfiniteQueryParam, | ||||
queryKeyFnName: camel(`get-${operationName}-infinite-query-key`), | ||||
}, | ||||
] | ||||
: []), | ||||
]; | ||||
|
||||
const queryKeyFnName = camel(`get-${operationName}-queryKey`); | ||||
// Convert "param: Type" to "param?: Type" for queryKey functions | ||||
// to enable cache invalidation without type assertion | ||||
const makeParamsOptional = (params: string) => { | ||||
|
@@ -1316,41 +1318,63 @@ const generateQueryHook = async ( | |||
); | ||||
}; | ||||
|
||||
const queryKeyProps = makeParamsOptional( | ||||
toObjectString( | ||||
props.filter((prop) => prop.type !== GetterPropType.HEADER), | ||||
'implementation', | ||||
), | ||||
const uniqueQueryOptionsByKeys = queries.filter( | ||||
(obj, index, self) => | ||||
index === | ||||
self.findIndex((t) => t.queryKeyFnName === obj.queryKeyFnName), | ||||
); | ||||
|
||||
const routeString = | ||||
isVue(outputClient) || override.query.shouldSplitQueryKey | ||||
? getRouteAsArray(route) // Note: this is required for reactivity to work, we will lose it if route params are converted into string, only as array they will be tracked // TODO: add tests for this | ||||
: `\`${route}\``; | ||||
|
||||
// Use operation ID as query key if enabled, otherwise use route string | ||||
const queryKeyIdentifier = override.query.useOperationIdAsQueryKey | ||||
? operationName | ||||
: routeString; | ||||
|
||||
// Note: do not unref() params in Vue - this will make key lose reactivity | ||||
const queryKeyFn = `${ | ||||
override.query.shouldExportQueryKey ? 'export ' : '' | ||||
}const ${queryKeyFnName} = (${queryKeyProps}) => { | ||||
return [${queryKeyIdentifier}${queryParams ? ', ...(params ? [params]: [])' : ''}${ | ||||
body.implementation ? `, ${body.implementation}` : '' | ||||
}] as const; | ||||
}`; | ||||
|
||||
implementation += `${queryKeyMutator ? '' : queryKeyFn} | ||||
|
||||
${queries.reduce( | ||||
(acc, queryOption) => | ||||
implementation += ` | ||||
${ | ||||
!queryKeyMutator | ||||
? uniqueQueryOptionsByKeys.reduce((acc, queryOption) => { | ||||
const queryKeyProps = makeParamsOptional( | ||||
toObjectString( | ||||
props.filter((prop) => prop.type !== GetterPropType.HEADER), | ||||
'implementation', | ||||
), | ||||
); | ||||
|
||||
const routeString = | ||||
isVue(outputClient) || override.query.shouldSplitQueryKey | ||||
? getRouteAsArray(route) // Note: this is required for reactivity to work, we will lose it if route params are converted into string, only as array they will be tracked // TODO: add tests for this | ||||
: `\`${route}\``; | ||||
|
||||
// Use operation ID as query key if enabled, otherwise use route string | ||||
const queryKeyIdentifier = override.query.useOperationIdAsQueryKey | ||||
? operationName | ||||
: routeString; | ||||
|
||||
// Note: do not unref() params in Vue - this will make key lose reactivity | ||||
const queryKeyFn = ` | ||||
${override.query.shouldExportQueryKey ? 'export ' : ''}const ${queryOption.queryKeyFnName} = (${queryKeyProps}) => { | ||||
return [ | ||||
${[ | ||||
queryOption.type === QueryType.INFINITE || | ||||
queryOption.type === QueryType.SUSPENSE_INFINITE | ||||
? `'infinate'` | ||||
: '', | ||||
queryKeyIdentifier, | ||||
queryParams ? '...(params ? [params]: [])' : '', | ||||
body.implementation, | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Previously, the value was used only if
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We drop falsy value in next line filter |
||||
] | ||||
.filter((x) => !!x) | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since all values are normalized, this process is unnecessary.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need drop falsy value (like a empty There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, that's because it's now treated as an array instead of a string. And since you need to check for existence anyway, it's no longer necessary to check for the existence of just |
||||
.join(', ')} | ||||
] as const; | ||||
} | ||||
`; | ||||
return acc + queryKeyFn; | ||||
}, '') | ||||
: '' | ||||
}`; | ||||
|
||||
implementation += ` | ||||
${queries.reduce((acc, queryOption) => { | ||||
return ( | ||||
acc + | ||||
generateQueryImplementation({ | ||||
queryOption, | ||||
operationName, | ||||
queryKeyFnName, | ||||
queryProperties, | ||||
queryKeyProperties, | ||||
params, | ||||
|
@@ -1378,9 +1402,9 @@ const generateQueryHook = async ( | |||
usePrefetch: query.usePrefetch, | ||||
useQuery: query.useQuery, | ||||
useInfinite: query.useInfinite, | ||||
}), | ||||
'', | ||||
)} | ||||
}) | ||||
); | ||||
}, '')} | ||||
`; | ||||
|
||||
mutators = | ||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
now each query type has own queryKeyFnName