Skip to content

Commit 9e6b893

Browse files
authored
feat(react-query): update to major v5 (#13)
1 parent 4a074fd commit 9e6b893

File tree

17 files changed

+298
-264
lines changed

17 files changed

+298
-264
lines changed

.github/workflows/release-alpha.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ on:
1515
version:
1616
type: string
1717
required: false
18-
description: If your build failed and the version is already exists you can set version of package manually, e.g. `3.0.0-alpha.0``. Use the prefix `alpha` otherwise you will get error.
18+
description: If your build failed and the version is already exists you can set version of package manually, e.g. `3.0.0-alpha.0`. Use the prefix `alpha` otherwise you will get error.
1919

2020
jobs:
2121
release:

package-lock.json

Lines changed: 147 additions & 180 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
"@gravity-ui/prettier-config": "^1.1.0",
5454
"@gravity-ui/tsconfig": "^1.0.0",
5555
"@swc/jest": "^0.2.36",
56-
"@tanstack/react-query": "^4.36.1",
56+
"@tanstack/react-query": "^5.51.23",
5757
"@types/jest": "^29.5.12",
5858
"@types/react": "^18.3.3",
5959
"eslint": "^8.57.0",
@@ -63,7 +63,7 @@
6363
"react": "^18.3.1"
6464
},
6565
"peerDependencies": {
66-
"@tanstack/react-query": "^4.0.0",
67-
"react": "^16.0.0 || ^17.0.0 || ^18.0.0"
66+
"@tanstack/react-query": "^5.0.0",
67+
"react": "^18.0.0"
6868
}
6969
}

src/core/types/DataSource.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type {idle} from '../constants';
22

3-
export type DataSourceKey = readonly unknown[];
3+
export type DataSourceKey = ReadonlyArray<unknown>;
44
export type DataSourceTag = string;
55

66
declare const errorHintSymbol: unique symbol;
@@ -31,7 +31,7 @@ export interface DataSource<
3131

3232
[errorHintSymbol]?: TError;
3333

34-
options?: TOptions;
34+
options?: Partial<TOptions>;
3535
[stateHintSymbol]?: TState;
3636
}
3737

src/core/utils/composeFullKey.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {composeKey} from './composeKey';
55
export const composeFullKey = <TDataSource extends AnyDataSource>(
66
dataSource: TDataSource,
77
params: DataSourceParams<TDataSource>,
8-
): readonly string[] => {
8+
): ReadonlyArray<string> => {
99
const tags = dataSource.tags?.(params) ?? [];
1010

1111
return [dataSource.name, ...tags, composeKey(dataSource, params)];

src/core/utils/composeKey.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// TODO(DakEnviy): Do not use react-query in core
2-
import {hashQueryKey} from '@tanstack/react-query';
2+
import {hashKey} from '@tanstack/react-query';
33

44
import {idle} from '../constants';
55
import type {AnyDataSource, DataSourceParams} from '../types/DataSource';
@@ -8,4 +8,4 @@ export const composeKey = <TDataSource extends AnyDataSource>(
88
dataSource: TDataSource,
99
params: DataSourceParams<TDataSource>,
1010
): string =>
11-
params === idle ? `${dataSource.name}:idle` : `${dataSource.name}(${hashQueryKey(params)})`;
11+
params === idle ? `${dataSource.name}:idle` : `${dataSource.name}(${hashKey(params)})`;

src/react-query/hooks/useQueryData.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,37 @@
11
import type {DataSourceOptions, DataSourceParams, DataSourceState} from '../../core';
22
import {useInfiniteQueryData} from '../impl/infinite/hooks';
3+
import type {AnyInfiniteQueryDataSource} from '../impl/infinite/types';
34
import {usePlainQueryData} from '../impl/plain/hooks';
45
import type {AnyQueryDataSource} from '../types';
6+
import {notReachable} from '../utils/notReachable';
57

68
import {useQueryContext} from './useQueryContext';
79

810
export const useQueryData = <TDataSource extends AnyQueryDataSource>(
911
dataSource: TDataSource,
1012
params: DataSourceParams<TDataSource>,
11-
options?: DataSourceOptions<TDataSource>,
13+
options?: Partial<DataSourceOptions<TDataSource>>,
1214
): DataSourceState<TDataSource> => {
1315
const context = useQueryContext();
1416

17+
const type = dataSource.type;
1518
let state: DataSourceState<AnyQueryDataSource> | undefined;
1619

1720
// Do not change data source type in the same hook call
18-
if (dataSource.type === 'plain') {
21+
if (type === 'plain') {
1922
// eslint-disable-next-line react-hooks/rules-of-hooks
2023
state = usePlainQueryData(context, dataSource, params, options);
21-
} else if (dataSource.type === 'infinite') {
24+
} else if (type === 'infinite') {
2225
// eslint-disable-next-line react-hooks/rules-of-hooks
23-
state = useInfiniteQueryData(context, dataSource, params, options);
26+
state = useInfiniteQueryData(
27+
context,
28+
dataSource,
29+
params,
30+
// TS can't calculate types in this place
31+
options as Partial<DataSourceOptions<AnyInfiniteQueryDataSource>> | undefined,
32+
);
2433
} else {
25-
throw new Error('Data Source type must be plain or infinite');
34+
return notReachable(type, `Data Source type must be plain or infinite, got: ${type}`);
2635
}
2736

2837
return state as DataSourceState<TDataSource>;
Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
import {useMemo} from 'react';
2-
31
import {useInfiniteQuery} from '@tanstack/react-query';
42

5-
import type {DataSourceContext, DataSourceOptions, DataSourceParams} from '../../../core';
3+
import type {
4+
DataSourceContext,
5+
DataSourceOptions,
6+
DataSourceParams,
7+
DataSourceState,
8+
} from '../../../core';
69

710
import type {AnyInfiniteQueryDataSource} from './types';
811
import {composeOptions, transformResult} from './utils';
@@ -11,10 +14,10 @@ export const useInfiniteQueryData = <TDataSource extends AnyInfiniteQueryDataSou
1114
context: DataSourceContext<TDataSource>,
1215
dataSource: TDataSource,
1316
params: DataSourceParams<TDataSource>,
14-
options?: DataSourceOptions<TDataSource>,
15-
) => {
17+
options?: Partial<DataSourceOptions<TDataSource>>,
18+
): DataSourceState<TDataSource> => {
1619
const composedOptions = composeOptions(context, dataSource, params, options);
1720
const result = useInfiniteQuery(composedOptions);
1821

19-
return useMemo(() => transformResult(result), [result]);
22+
return transformResult(result);
2023
};
Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type {
2+
InfiniteData,
23
InfiniteQueryObserverOptions,
34
InfiniteQueryObserverResult,
45
QueryFunctionContext,
@@ -15,22 +16,51 @@ export type InfiniteQueryDataSource<TParams, TRequest, TResponse, TData, TError>
1516
TResponse,
1617
TData,
1718
TError,
18-
InfiniteQueryObserverOptions<TResponse, TError, ActualData<TData, TResponse>, TResponse>,
19-
ResultWrapper<InfiniteQueryObserverResult<ActualData<TData, TResponse>, TError>>,
20-
QueryFunctionContext<DataSourceKey, Partial<TRequest> | undefined>
19+
InfiniteQueryObserverOptions<
20+
TResponse,
21+
TError,
22+
InfiniteData<ActualData<TData, TResponse>, Partial<TRequest>>,
23+
TResponse,
24+
DataSourceKey,
25+
Partial<TRequest>
26+
>,
27+
ResultWrapper<
28+
InfiniteQueryObserverResult<
29+
InfiniteData<ActualData<TData, TResponse>, Partial<TRequest>>,
30+
TError
31+
>,
32+
TRequest,
33+
TResponse,
34+
TData,
35+
TError
36+
>,
37+
QueryFunctionContext<DataSourceKey, Partial<TRequest>>
2138
> & {
2239
type: 'infinite';
23-
next: (lastPage: TResponse, allPages: TResponse[]) => Partial<TRequest> | undefined;
24-
prev?: (firstPage: TResponse, allPages: TResponse[]) => Partial<TRequest> | undefined;
40+
next: (lastPage: TResponse, allPages: TResponse[]) => Partial<TRequest> | null | undefined;
41+
prev?: (firstPage: TResponse, allPages: TResponse[]) => Partial<TRequest> | null | undefined;
2542
};
2643

2744
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2845
export type AnyInfiniteQueryDataSource = InfiniteQueryDataSource<any, any, any, any, any>;
2946

30-
type ResultWrapper<T> =
31-
T extends InfiniteQueryObserverResult<infer TData>
32-
? Overwrite<T, {status: DataLoaderStatus; data: TData}> & {
33-
originalStatus: T['status'];
34-
originalData: T['data'];
47+
// It is used instead of `Partial<DataSourceRequest<TDataSource>>` because TS can't calculate type
48+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
49+
export type AnyPageParam = Partial<any>;
50+
51+
type ResultWrapper<TResult, TRequest, TResponse, TData, TError> =
52+
TResult extends InfiniteQueryObserverResult<
53+
InfiniteData<ActualData<TData, TResponse>, Partial<TRequest>>,
54+
TError
55+
>
56+
? Overwrite<
57+
TResult,
58+
{
59+
status: DataLoaderStatus;
60+
data: Array<FlatArray<Array<ActualData<TData, TResponse>>, 1>>;
61+
}
62+
> & {
63+
originalStatus: TResult['status'];
64+
originalData: TResult['data'];
3565
}
3666
: never;

src/react-query/impl/infinite/utils.ts

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import {skipToken} from '@tanstack/react-query';
12
import type {
3+
InfiniteData,
24
InfiniteQueryObserverOptions,
35
InfiniteQueryObserverResult,
46
QueryFunctionContext,
@@ -12,63 +14,64 @@ import type {
1214
DataSourceKey,
1315
DataSourceOptions,
1416
DataSourceParams,
15-
DataSourceRequest,
1617
DataSourceResponse,
1718
DataSourceState,
1819
} from '../../../core';
1920
import {normalizeStatus} from '../../utils/normalizeStatus';
2021

21-
import type {AnyInfiniteQueryDataSource} from './types';
22+
import type {AnyInfiniteQueryDataSource, AnyPageParam} from './types';
2223

2324
const EMPTY_ARRAY: unknown[] = [];
25+
const EMPTY_OBJECT = {};
2426

2527
export const composeOptions = <TDataSource extends AnyInfiniteQueryDataSource>(
2628
context: DataSourceContext<TDataSource>,
2729
dataSource: TDataSource,
2830
params: DataSourceParams<TDataSource>,
29-
options?: DataSourceOptions<TDataSource>,
31+
options?: Partial<DataSourceOptions<TDataSource>>,
3032
): InfiniteQueryObserverOptions<
3133
DataSourceResponse<TDataSource>,
3234
DataSourceError<TDataSource>,
33-
DataSourceData<TDataSource>,
34-
DataSourceResponse<TDataSource>
35+
InfiniteData<DataSourceData<TDataSource>, AnyPageParam>,
36+
DataSourceResponse<TDataSource>,
37+
DataSourceKey,
38+
AnyPageParam
3539
> => {
3640
const {transformParams, transformResponse, next, prev} = dataSource;
3741

42+
const queryFn = (
43+
fetchContext: QueryFunctionContext<DataSourceKey, AnyPageParam>,
44+
): DataSourceResponse<TDataSource> | Promise<DataSourceResponse<TDataSource>> => {
45+
const request = transformParams ? transformParams(params) : params;
46+
const paginatedRequest = {...request, ...fetchContext.pageParam};
47+
48+
return dataSource.fetch(context, fetchContext, paginatedRequest);
49+
};
50+
3851
return {
39-
...dataSource.options,
40-
enabled: params !== idle,
4152
queryKey: composeFullKey(dataSource, params),
42-
queryFn: (
43-
fetchContext: QueryFunctionContext<
44-
DataSourceKey,
45-
Partial<DataSourceRequest<TDataSource>> | undefined
46-
>,
47-
) => {
48-
const actualParams = transformParams ? transformParams(params) : params;
49-
const request =
50-
typeof actualParams === 'object'
51-
? {...actualParams, ...fetchContext.pageParam}
52-
: actualParams;
53-
54-
return dataSource.fetch(context, fetchContext, request);
55-
},
53+
queryFn: params === idle ? skipToken : queryFn,
5654
select: transformResponse
5755
? (data) => ({...data, pages: data.pages.map(transformResponse)})
5856
: undefined,
57+
initialPageParam: EMPTY_OBJECT,
5958
getNextPageParam: next,
6059
getPreviousPageParam: prev,
60+
...dataSource.options,
6161
...options,
6262
};
6363
};
6464

6565
export const transformResult = <TDataSource extends AnyInfiniteQueryDataSource>(
66-
result: InfiniteQueryObserverResult<DataSourceData<TDataSource>, DataSourceError<TDataSource>>,
67-
) => {
66+
result: InfiniteQueryObserverResult<
67+
InfiniteData<DataSourceData<TDataSource>, AnyPageParam>,
68+
DataSourceError<TDataSource>
69+
>,
70+
): DataSourceState<TDataSource> => {
6871
return {
6972
...result,
7073
status: normalizeStatus(result.status, result.fetchStatus),
71-
data: result.data?.pages.flat() ?? EMPTY_ARRAY,
74+
data: result.data?.pages.flat(1) ?? EMPTY_ARRAY,
7275
originalStatus: result.status,
7376
originalData: result.data,
7477
} as DataSourceState<TDataSource>;

0 commit comments

Comments
 (0)