Skip to content

Commit 4820b24

Browse files
committed
feat: adds uniqueSuspenseQueryKeys option
1 parent 4d3fe11 commit 4820b24

File tree

10 files changed

+303
-31
lines changed

10 files changed

+303
-31
lines changed

.changeset/funny-nails-do.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@graphql-codegen/typescript-react-query': minor
3+
---
4+
5+
adds uniqueSuspenseQueryKeys config to control suspense query key generation

packages/plugins/typescript/react-query/src/config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,13 @@ export interface BaseReactQueryPluginConfig {
8383
*/
8484
addSuspenseQuery?: boolean;
8585

86+
/**
87+
* @default true
88+
* @description Whether suspense queries have unique keys compared to standard queries.
89+
* If true a suspense query will have "Suspense" appended to its query key, otherwise it will have the same query key as it's standard query variant.
90+
*/
91+
uniqueSuspenseQueryKeys?: boolean;
92+
8693
/**
8794
* @default false
8895
* @description If true, it imports `react-query` not `@tanstack/react-query`, default is false.

packages/plugins/typescript/react-query/src/fetcher-custom-mapper.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,11 @@ export class CustomMapperFetcher extends FetcherRenderer {
4646
return null;
4747
}
4848

49-
generateInfiniteQueryHook(config: GenerateConfig, isSuspense = false): string {
49+
generateInfiniteQueryHook(
50+
config: GenerateConfig,
51+
isSuspense = false,
52+
uniqueSuspenseQueryKeys: boolean,
53+
): string {
5054
const { documentVariableName, operationResultType, operationVariablesTypes } = config;
5155

5256
const typedFetcher = this.getFetcherFnName(operationResultType, operationVariablesTypes);
@@ -57,16 +61,28 @@ export class CustomMapperFetcher extends FetcherRenderer {
5761
? `(metaData) => query({...variables, ...(metaData.pageParam ?? {})})`
5862
: `(metaData) => ${typedFetcher}(${documentVariableName}, {...variables, ...(metaData.pageParam ?? {})})()`;
5963

60-
const { generateBaseInfiniteQueryHook } = this.generateInfiniteQueryHelper(config, isSuspense);
64+
const { generateBaseInfiniteQueryHook } = this.generateInfiniteQueryHelper(
65+
config,
66+
isSuspense,
67+
uniqueSuspenseQueryKeys,
68+
);
6169

6270
return generateBaseInfiniteQueryHook({
6371
implHookOuter,
6472
implFetcher,
6573
});
6674
}
6775

68-
generateQueryHook(config: GenerateConfig, isSuspense = false): string {
69-
const { generateBaseQueryHook } = this.generateQueryHelper(config, isSuspense);
76+
generateQueryHook(
77+
config: GenerateConfig,
78+
isSuspense = false,
79+
uniqueSuspenseQueryKeys: boolean,
80+
): string {
81+
const { generateBaseQueryHook } = this.generateQueryHelper(
82+
config,
83+
isSuspense,
84+
uniqueSuspenseQueryKeys,
85+
);
7086

7187
const { documentVariableName, operationResultType, operationVariablesTypes } = config;
7288

packages/plugins/typescript/react-query/src/fetcher-fetch-hardcoded.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,16 @@ ${this.getFetchParams()}
5959
}`;
6060
}
6161

62-
generateInfiniteQueryHook(config: GenerateConfig, isSuspense = false): string {
63-
const { generateBaseInfiniteQueryHook } = this.generateInfiniteQueryHelper(config, isSuspense);
62+
generateInfiniteQueryHook(
63+
config: GenerateConfig,
64+
isSuspense = false,
65+
uniqueSuspenseQueryKeys: boolean,
66+
): string {
67+
const { generateBaseInfiniteQueryHook } = this.generateInfiniteQueryHelper(
68+
config,
69+
isSuspense,
70+
uniqueSuspenseQueryKeys,
71+
);
6472

6573
const { documentVariableName, operationResultType, operationVariablesTypes } = config;
6674

@@ -69,8 +77,16 @@ ${this.getFetchParams()}
6977
});
7078
}
7179

72-
generateQueryHook(config: GenerateConfig, isSuspense = false): string {
73-
const { generateBaseQueryHook } = this.generateQueryHelper(config, isSuspense);
80+
generateQueryHook(
81+
config: GenerateConfig,
82+
isSuspense = false,
83+
uniqueSuspenseQueryKeys: boolean,
84+
): string {
85+
const { generateBaseQueryHook } = this.generateQueryHelper(
86+
config,
87+
isSuspense,
88+
uniqueSuspenseQueryKeys,
89+
);
7490

7591
const { documentVariableName, operationResultType, operationVariablesTypes } = config;
7692

packages/plugins/typescript/react-query/src/fetcher-fetch.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,15 @@ function fetcher<TData, TVariables>(endpoint: string, requestInit: RequestInit,
3131
}`;
3232
}
3333

34-
generateInfiniteQueryHook(config: GenerateConfig, isSuspense = false): string {
34+
generateInfiniteQueryHook(
35+
config: GenerateConfig,
36+
isSuspense = false,
37+
uniqueSuspenseQueryKeys: boolean,
38+
): string {
3539
const { generateBaseInfiniteQueryHook, variables, options } = this.generateInfiniteQueryHelper(
3640
config,
3741
isSuspense,
42+
uniqueSuspenseQueryKeys,
3843
);
3944

4045
const { documentVariableName, operationResultType, operationVariablesTypes } = config;
@@ -49,10 +54,15 @@ function fetcher<TData, TVariables>(endpoint: string, requestInit: RequestInit,
4954
});
5055
}
5156

52-
generateQueryHook(config: GenerateConfig, isSuspense = false): string {
57+
generateQueryHook(
58+
config: GenerateConfig,
59+
isSuspense = false,
60+
uniqueSuspenseQueryKeys: boolean,
61+
): string {
5362
const { generateBaseQueryHook, variables, options } = this.generateQueryHelper(
5463
config,
5564
isSuspense,
65+
uniqueSuspenseQueryKeys,
5666
);
5767

5868
const { documentVariableName, operationResultType, operationVariablesTypes } = config;

packages/plugins/typescript/react-query/src/fetcher-graphql-request.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,19 @@ function fetcher<TData, TVariables extends { [key: string]: any }>(client: Graph
3535
}`;
3636
}
3737

38-
generateInfiniteQueryHook(config: GenerateConfig, isSuspense = false): string {
38+
generateInfiniteQueryHook(
39+
config: GenerateConfig,
40+
isSuspense = false,
41+
uniqueSuspenseQueryKeys: boolean,
42+
): string {
3943
const typeImport = this.visitor.config.useTypeImports ? 'import type' : 'import';
4044
if (this.clientPath) this.visitor.imports.add(this.clientPath);
4145
this.visitor.imports.add(`${typeImport} { GraphQLClient } from 'graphql-request';`);
4246

4347
const { generateBaseInfiniteQueryHook, variables, options } = this.generateInfiniteQueryHelper(
4448
config,
4549
isSuspense,
50+
uniqueSuspenseQueryKeys,
4651
);
4752

4853
const { documentVariableName, operationResultType, operationVariablesTypes } = config;
@@ -68,7 +73,11 @@ function fetcher<TData, TVariables extends { [key: string]: any }>(client: Graph
6873
});
6974
}
7075

71-
generateQueryHook(config: GenerateConfig, isSuspense = false): string {
76+
generateQueryHook(
77+
config: GenerateConfig,
78+
isSuspense = false,
79+
uniqueSuspenseQueryKeys: boolean,
80+
): string {
7281
const typeImport = this.visitor.config.useTypeImports ? 'import type' : 'import';
7382
if (this.clientPath) this.visitor.imports.add(this.clientPath);
7483
this.visitor.imports.add(`${typeImport} { GraphQLClient } from 'graphql-request';`);
@@ -79,6 +88,7 @@ function fetcher<TData, TVariables extends { [key: string]: any }>(client: Graph
7988
const { generateBaseQueryHook, variables, options } = this.generateQueryHelper(
8089
config,
8190
isSuspense,
91+
uniqueSuspenseQueryKeys,
8292
);
8393

8494
const { documentVariableName, operationResultType, operationVariablesTypes } = config;

packages/plugins/typescript/react-query/src/fetcher.ts

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,15 @@ export abstract class FetcherRenderer {
3232

3333
public abstract generateFetcherImplementation(): string;
3434
public abstract generateFetcherFetch(config: GenerateConfig): string;
35-
protected abstract generateQueryHook(config: GenerateConfig, isSuspense?: boolean): string;
35+
protected abstract generateQueryHook(
36+
config: GenerateConfig,
37+
isSuspense: boolean,
38+
uniqueSuspenseQueryKeys: boolean,
39+
): string;
3640
protected abstract generateInfiniteQueryHook(
3741
config: GenerateConfig,
38-
isSuspense?: boolean,
42+
isSuspense: boolean,
43+
uniqueSuspenseQueryKeys: boolean,
3944
): string;
4045
protected abstract generateMutationHook(config: GenerateConfig): string;
4146

@@ -60,7 +65,11 @@ export abstract class FetcherRenderer {
6065
return queryMethodMap;
6166
}
6267

63-
protected generateInfiniteQueryHelper(config: GenerateConfig, isSuspense: boolean) {
68+
protected generateInfiniteQueryHelper(
69+
config: GenerateConfig,
70+
isSuspense: boolean,
71+
uniqueSuspenseQueryKeys: boolean,
72+
) {
6473
const { operationResultType, operationName } = config;
6574

6675
const { infiniteQuery } = this.createQueryMethodMap(isSuspense);
@@ -99,7 +108,7 @@ export abstract class FetcherRenderer {
99108
${implHookOuter}
100109
return ${infiniteQuery.getHook()}<${operationResultType}, TError, TData>(
101110
${this.generateInfiniteQueryFormattedParameters(
102-
this.generateInfiniteQueryKey(config, isSuspense),
111+
this.generateInfiniteQueryKey(config, isSuspense, uniqueSuspenseQueryKeys),
103112
implFetcher,
104113
)}
105114
)};`;
@@ -108,7 +117,11 @@ export abstract class FetcherRenderer {
108117
return { generateBaseInfiniteQueryHook, variables, options };
109118
}
110119

111-
protected generateQueryHelper(config: GenerateConfig, isSuspense: boolean) {
120+
protected generateQueryHelper(
121+
config: GenerateConfig,
122+
isSuspense: boolean,
123+
uniqueSuspenseQueryKeys: boolean,
124+
) {
112125
const { operationName, operationResultType } = config;
113126

114127
const { query } = this.createQueryMethodMap(isSuspense);
@@ -136,7 +149,7 @@ export abstract class FetcherRenderer {
136149
${implHookOuter}
137150
return ${query.getHook()}<${operationResultType}, TError, TData>(
138151
${this.generateQueryFormattedParameters(
139-
this.generateQueryKey(config, isSuspense),
152+
this.generateQueryKey(config, isSuspense, uniqueSuspenseQueryKeys),
140153
implFetcher,
141154
)}
142155
)};`;
@@ -222,42 +235,63 @@ export abstract class FetcherRenderer {
222235
return `options: Omit<${infiniteQuery.getOptions()}<${operationResultType}, TError, TData>, 'queryKey'> & { queryKey?: ${infiniteQuery.getOptions()}<${operationResultType}, TError, TData>['queryKey'] }`;
223236
}
224237

225-
public generateInfiniteQueryKey(config: GenerateConfig, isSuspense: boolean): string {
226-
const identifier = isSuspense ? 'infiniteSuspense' : 'infinite';
238+
public generateInfiniteQueryKey(
239+
config: GenerateConfig,
240+
isSuspense: boolean,
241+
uniqueSuspenseQueryKeys: boolean,
242+
): string {
243+
const identifier = isSuspense
244+
? `infinite${uniqueSuspenseQueryKeys ? 'Suspense' : ''}`
245+
: 'infinite';
227246
if (config.hasRequiredVariables)
228247
return `['${config.node.name.value}.${identifier}', variables]`;
229248
return `variables === undefined ? ['${config.node.name.value}.${identifier}'] : ['${config.node.name.value}.${identifier}', variables]`;
230249
}
231250

232-
public generateInfiniteQueryOutput(config: GenerateConfig, isSuspense = false) {
251+
public generateInfiniteQueryOutput(
252+
config: GenerateConfig,
253+
isSuspense = false,
254+
uniqueSuspenseQueryKeys: boolean,
255+
) {
233256
const { infiniteQuery } = this.createQueryMethodMap(isSuspense);
234257
const signature = this.generateQueryVariablesSignature(config);
235258
const { operationName, node } = config;
236259
return {
237-
hook: this.generateInfiniteQueryHook(config, isSuspense),
260+
hook: this.generateInfiniteQueryHook(config, isSuspense, uniqueSuspenseQueryKeys),
238261
getKey: `${infiniteQuery.getHook(
239262
operationName,
240-
)}.getKey = (${signature}) => ${this.generateInfiniteQueryKey(config, isSuspense)};`,
263+
)}.getKey = (${signature}) => ${this.generateInfiniteQueryKey(config, isSuspense, uniqueSuspenseQueryKeys)};`,
241264
rootKey: `${infiniteQuery.getHook(operationName)}.rootKey = '${node.name.value}.infinite';`,
242265
};
243266
}
244267

245-
public generateQueryKey(config: GenerateConfig, isSuspense: boolean): string {
246-
const identifier = isSuspense ? `${config.node.name.value}Suspense` : config.node.name.value;
268+
public generateQueryKey(
269+
config: GenerateConfig,
270+
isSuspense: boolean,
271+
uniqueSuspenseQueryKeys: boolean,
272+
): string {
273+
const identifier = isSuspense
274+
? `${config.node.name.value}${uniqueSuspenseQueryKeys ? 'Suspense' : ''}`
275+
: config.node.name.value;
247276
if (config.hasRequiredVariables) return `['${identifier}', variables]`;
248277
return `variables === undefined ? ['${identifier}'] : ['${identifier}', variables]`;
249278
}
250279

251-
public generateQueryOutput(config: GenerateConfig, isSuspense = false) {
280+
public generateQueryOutput(
281+
config: GenerateConfig,
282+
isSuspense = false,
283+
uniqueSuspenseQueryKeys: boolean,
284+
) {
252285
const { query } = this.createQueryMethodMap(isSuspense);
253286
const signature = this.generateQueryVariablesSignature(config);
254287
const { operationName, node, documentVariableName } = config;
255288
return {
256-
hook: this.generateQueryHook(config, isSuspense),
289+
hook: this.generateQueryHook(config, isSuspense, uniqueSuspenseQueryKeys),
257290
document: `${query.getHook(operationName)}.document = ${documentVariableName};`,
258291
getKey: `${query.getHook(operationName)}.getKey = (${signature}) => ${this.generateQueryKey(
259292
config,
260293
isSuspense,
294+
uniqueSuspenseQueryKeys,
261295
)};`,
262296
rootKey: `${query.getHook(operationName)}.rootKey = '${node.name.value}';`,
263297
};

packages/plugins/typescript/react-query/src/visitor.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export class ReactQueryVisitor extends ClientSideBaseVisitor<
4545
exposeFetcher: getConfigValue(rawConfig.exposeFetcher, false),
4646
addInfiniteQuery: getConfigValue(rawConfig.addInfiniteQuery, false),
4747
addSuspenseQuery: getConfigValue(rawConfig.addSuspenseQuery, false),
48+
uniqueSuspenseQueryKeys: getConfigValue(rawConfig.uniqueSuspenseQueryKeys, true),
4849
reactQueryVersion: getConfigValue(rawConfig.reactQueryVersion, defaultReactQueryVersion),
4950
reactQueryImportFrom: getConfigValue(rawConfig.reactQueryImportFrom, ''),
5051
});
@@ -149,10 +150,15 @@ export class ReactQueryVisitor extends ClientSideBaseVisitor<
149150
const getOutputFromQueries = () => `\n${queries.join('\n\n')}\n`;
150151

151152
if (operationType === 'Query') {
152-
const addQuery = (generateConfig: GenerateConfig, isSuspense = false) => {
153+
const addQuery = (
154+
generateConfig: GenerateConfig,
155+
isSuspense = false,
156+
uniqueSuspenseQueryKeys: boolean = true,
157+
) => {
153158
const { hook, getKey, rootKey, document } = this.fetcher.generateQueryOutput(
154159
generateConfig,
155160
isSuspense,
161+
uniqueSuspenseQueryKeys,
156162
);
157163
queries.push(hook);
158164
if (this.config.exposeDocument) queries.push(document);
@@ -162,13 +168,19 @@ export class ReactQueryVisitor extends ClientSideBaseVisitor<
162168

163169
addQuery(generateConfig);
164170

165-
if (this.config.addSuspenseQuery) addQuery(generateConfig, true);
171+
if (this.config.addSuspenseQuery)
172+
addQuery(generateConfig, true, this.config.uniqueSuspenseQueryKeys);
166173

167174
if (this.config.addInfiniteQuery) {
168-
const addInfiniteQuery = (generateConfig: GenerateConfig, isSuspense = false) => {
175+
const addInfiniteQuery = (
176+
generateConfig: GenerateConfig,
177+
isSuspense = false,
178+
uniqueSuspenseQueryKeys: boolean = true,
179+
) => {
169180
const { hook, getKey, rootKey } = this.fetcher.generateInfiniteQueryOutput(
170181
generateConfig,
171182
isSuspense,
183+
uniqueSuspenseQueryKeys,
172184
);
173185
queries.push(hook);
174186
if (this.config.exposeQueryKeys) queries.push(getKey);
@@ -178,7 +190,7 @@ export class ReactQueryVisitor extends ClientSideBaseVisitor<
178190
addInfiniteQuery(generateConfig);
179191

180192
if (this.config.addSuspenseQuery) {
181-
addInfiniteQuery(generateConfig, true);
193+
addInfiniteQuery(generateConfig, true, this.config.uniqueSuspenseQueryKeys);
182194
}
183195
}
184196
// The reason we're looking at the private field of the CustomMapperFetcher to see if it's a react hook

0 commit comments

Comments
 (0)