@ainias42/tanstack-ids 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.cjs +2 -1
- package/.prettierrc +1 -1
- package/dist/QueryContext/QueriesContextProvider.d.ts +1 -1
- package/dist/QueryContext/QueriesContextProvider.js +1 -1
- package/dist/QueryContext/QueriesContextProvider.js.map +1 -1
- package/dist/QueryContext/SubQueriesContext.d.ts +1 -1
- package/dist/QueryContext/SubQueriesContext.js +5 -5
- package/dist/QueryContext/SubQueriesContext.js.map +1 -1
- package/dist/QueryContext/useGenerateQueriesContextId.js +1 -1
- package/dist/QueryContext/useGenerateQueriesContextId.js.map +1 -1
- package/dist/QueryContext/useQueriesError.js +1 -1
- package/dist/QueryContext/useQueriesError.js.map +1 -1
- package/dist/QueryContext/useQueriesIsFetching.js +1 -1
- package/dist/QueryContext/useQueriesIsFetching.js.map +1 -1
- package/dist/QueryContext/useQueriesReload.js +1 -1
- package/dist/QueryContext/useQueriesReload.js.map +1 -1
- package/dist/QueryContext/useQueriesStore.js +17 -17
- package/dist/QueryContext/useQueriesStore.js.map +1 -1
- package/dist/QueryContext/useQuerySubscribeToReload.js +7 -6
- package/dist/QueryContext/useQuerySubscribeToReload.js.map +1 -1
- package/dist/QueryContext/useScopedQueriesIsFetching.js +1 -1
- package/dist/QueryContext/useScopedQueriesIsFetching.js.map +1 -1
- package/dist/generateIdsQuery.d.ts +129 -23
- package/dist/generateIdsQuery.js +89 -36
- package/dist/generateIdsQuery.js.map +1 -1
- package/dist/generateNestedIdQuery.d.ts +126 -21
- package/dist/generateNestedIdQuery.js +2 -2
- package/dist/generateNestedIdQuery.js.map +1 -1
- package/dist/generateQuery.js +1 -1
- package/dist/generateQuery.js.map +1 -1
- package/dist/hooks/formatInitialPage.d.ts +10 -0
- package/dist/hooks/formatInitialPage.js +13 -0
- package/dist/hooks/formatInitialPage.js.map +1 -0
- package/dist/hooks/useArrayData.js +1 -1
- package/dist/hooks/useArrayData.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -2
- package/src/QueryContext/QueriesContextProvider.tsx +2 -2
- package/src/QueryContext/SubQueriesContext.tsx +7 -7
- package/src/QueryContext/useGenerateQueriesContextId.ts +1 -1
- package/src/QueryContext/useQueriesError.ts +1 -1
- package/src/QueryContext/useQueriesIsFetching.ts +1 -1
- package/src/QueryContext/useQueriesReload.ts +1 -1
- package/src/QueryContext/useQueriesStore.ts +21 -21
- package/src/QueryContext/useQuerySubscribeToReload.ts +7 -6
- package/src/QueryContext/useScopedQueriesIsFetching.ts +1 -1
- package/src/generateIdsQuery.ts +213 -95
- package/src/generateNestedIdQuery.ts +4 -4
- package/src/generateQuery.ts +12 -13
- package/src/hoistReactQueryKitAttributes.ts +1 -1
- package/src/hooks/formatInitialPage.ts +12 -0
- package/src/hooks/useArrayData.ts +3 -3
- package/src/hooks/useIndexedData.ts +3 -3
- package/src/index.ts +1 -0
- package/tsconfig.json +3 -12
|
@@ -4,5 +4,5 @@ import { useQueriesStore } from '@/QueryContext/useQueriesStore';
|
|
|
4
4
|
|
|
5
5
|
export function useQueriesIsFetching(contextId?: number) {
|
|
6
6
|
const context = useContext(QueriesContext);
|
|
7
|
-
return useQueriesStore(s => s.isFetching(contextId ?? context));
|
|
7
|
+
return useQueriesStore((s) => s.isFetching(contextId ?? context));
|
|
8
8
|
}
|
|
@@ -5,7 +5,7 @@ import { useQueryClient } from '@tanstack/react-query';
|
|
|
5
5
|
|
|
6
6
|
export function useQueriesReload(contextId?: number) {
|
|
7
7
|
const context = useContext(QueriesContext);
|
|
8
|
-
const reloadContext = useQueriesStore(s => s.reload);
|
|
8
|
+
const reloadContext = useQueriesStore((s) => s.reload);
|
|
9
9
|
const queryClient = useQueryClient();
|
|
10
10
|
|
|
11
11
|
return useCallback(async () => {
|
|
@@ -21,14 +21,14 @@ function initialState() {
|
|
|
21
21
|
items: URecord<number, QueryItem>;
|
|
22
22
|
}
|
|
23
23
|
>,
|
|
24
|
-
subContexts: {} as URecord<number, number[]
|
|
24
|
+
subContexts: {} as URecord<number, number[]>,
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export type QueriesStoreState = ReturnType<typeof actionsGenerator> & ReturnType<typeof initialState>;
|
|
29
29
|
type SetState = (
|
|
30
30
|
newState: Partial<QueriesStoreState> | QueriesStoreState | ((state: QueriesStoreState) => QueriesStoreState | void),
|
|
31
|
-
replace?: boolean
|
|
31
|
+
replace?: boolean,
|
|
32
32
|
) => void;
|
|
33
33
|
type GetState = () => Readonly<QueriesStoreState>;
|
|
34
34
|
const actionsGenerator = (set: SetState, get: GetState) => ({
|
|
@@ -38,11 +38,11 @@ const actionsGenerator = (set: SetState, get: GetState) => ({
|
|
|
38
38
|
},
|
|
39
39
|
generateContextId() {
|
|
40
40
|
let id = 0;
|
|
41
|
-
set(state => {
|
|
41
|
+
set((state) => {
|
|
42
42
|
state.lastContextId++;
|
|
43
43
|
state.contexts[state.lastContextId] = {
|
|
44
44
|
lastId: 0,
|
|
45
|
-
items: {}
|
|
45
|
+
items: {},
|
|
46
46
|
};
|
|
47
47
|
id = state.lastContextId;
|
|
48
48
|
});
|
|
@@ -50,7 +50,7 @@ const actionsGenerator = (set: SetState, get: GetState) => ({
|
|
|
50
50
|
},
|
|
51
51
|
setItem(contextId: number, itemId: number | undefined, item: QueryItem) {
|
|
52
52
|
let id = itemId;
|
|
53
|
-
set(state => {
|
|
53
|
+
set((state) => {
|
|
54
54
|
const contextData = state.contexts[contextId];
|
|
55
55
|
if (!contextData) {
|
|
56
56
|
return;
|
|
@@ -68,34 +68,34 @@ const actionsGenerator = (set: SetState, get: GetState) => ({
|
|
|
68
68
|
if (!itemId) {
|
|
69
69
|
return;
|
|
70
70
|
}
|
|
71
|
-
set(state => {
|
|
71
|
+
set((state) => {
|
|
72
72
|
delete state.contexts[contextId]?.items[itemId];
|
|
73
73
|
});
|
|
74
74
|
},
|
|
75
75
|
isFetching(contextId: number): boolean {
|
|
76
76
|
const state = get();
|
|
77
77
|
return (
|
|
78
|
-
ObjectHelper.values(state.contexts[contextId]?.items ?? {}).some(item => item.isFetching) ||
|
|
79
|
-
(state.subContexts[contextId]?.some(id => state.isFetching(id)) ?? false)
|
|
78
|
+
ObjectHelper.values(state.contexts[contextId]?.items ?? {}).some((item) => item.isFetching) ||
|
|
79
|
+
(state.subContexts[contextId]?.some((id) => state.isFetching(id)) ?? false)
|
|
80
80
|
);
|
|
81
81
|
},
|
|
82
82
|
isFetchingByScope(contextId: number, scope: { global: boolean; subContexts: boolean }): boolean {
|
|
83
83
|
const state = get();
|
|
84
84
|
return (
|
|
85
85
|
((scope.global &&
|
|
86
|
-
ObjectHelper.values(state.contexts[contextId]?.items ?? {}).some(item => item.isFetching)) ||
|
|
87
|
-
(scope.subContexts && state.subContexts[contextId]?.some(id => state.isFetching(id)))) ??
|
|
86
|
+
ObjectHelper.values(state.contexts[contextId]?.items ?? {}).some((item) => item.isFetching)) ||
|
|
87
|
+
(scope.subContexts && state.subContexts[contextId]?.some((id) => state.isFetching(id)))) ??
|
|
88
88
|
false
|
|
89
89
|
);
|
|
90
90
|
},
|
|
91
91
|
getError(contextId: number): any {
|
|
92
92
|
const state = get();
|
|
93
93
|
|
|
94
|
-
const error = ObjectHelper.values(state.contexts[contextId]?.items ?? {}).find(item => item.error)?.error;
|
|
94
|
+
const error = ObjectHelper.values(state.contexts[contextId]?.items ?? {}).find((item) => item.error)?.error;
|
|
95
95
|
if (error) {
|
|
96
96
|
return error;
|
|
97
97
|
}
|
|
98
|
-
const subContextWithError = state.subContexts[contextId]?.find(childId => state.getError(childId));
|
|
98
|
+
const subContextWithError = state.subContexts[contextId]?.find((childId) => state.getError(childId));
|
|
99
99
|
if (subContextWithError) {
|
|
100
100
|
return state.getError(subContextWithError);
|
|
101
101
|
}
|
|
@@ -104,7 +104,7 @@ const actionsGenerator = (set: SetState, get: GetState) => ({
|
|
|
104
104
|
getAllKeys(contextId: number): Set<string> {
|
|
105
105
|
const state = get();
|
|
106
106
|
|
|
107
|
-
const keys = new Set(ObjectHelper.values(state.contexts[contextId]?.items ?? {}).flatMap(item => item.keys));
|
|
107
|
+
const keys = new Set(ObjectHelper.values(state.contexts[contextId]?.items ?? {}).flatMap((item) => item.keys));
|
|
108
108
|
|
|
109
109
|
const subContext = state.subContexts[contextId];
|
|
110
110
|
if (subContext) {
|
|
@@ -123,28 +123,28 @@ const actionsGenerator = (set: SetState, get: GetState) => ({
|
|
|
123
123
|
|
|
124
124
|
const keys = state.getAllKeys(contextId);
|
|
125
125
|
return queryClient.refetchQueries({
|
|
126
|
-
predicate: query => keys.has(query.queryHash)
|
|
126
|
+
predicate: (query) => keys.has(query.queryHash),
|
|
127
127
|
});
|
|
128
128
|
},
|
|
129
129
|
addSubContext(parent: number, child: number) {
|
|
130
|
-
set(state => {
|
|
130
|
+
set((state) => {
|
|
131
131
|
const newChildren = state.subContexts[parent] ?? [];
|
|
132
132
|
newChildren.push(child);
|
|
133
133
|
state.subContexts[parent] = newChildren;
|
|
134
134
|
});
|
|
135
135
|
},
|
|
136
136
|
removeSubContext(parent: number, child: number) {
|
|
137
|
-
set(state => {
|
|
138
|
-
state.subContexts[parent] = state.subContexts[parent]?.filter(id => id !== child);
|
|
137
|
+
set((state) => {
|
|
138
|
+
state.subContexts[parent] = state.subContexts[parent]?.filter((id) => id !== child);
|
|
139
139
|
});
|
|
140
|
-
}
|
|
140
|
+
},
|
|
141
141
|
});
|
|
142
142
|
|
|
143
143
|
export const useQueriesStore = create<QueriesStoreState>()(
|
|
144
144
|
subscribeWithSelector(
|
|
145
145
|
immer((set, get) => ({
|
|
146
146
|
...initialState(),
|
|
147
|
-
...actionsGenerator(set, get)
|
|
148
|
-
}))
|
|
149
|
-
)
|
|
147
|
+
...actionsGenerator(set, get),
|
|
148
|
+
})),
|
|
149
|
+
),
|
|
150
150
|
);
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { QueriesContext } from '@/QueryContext/QueriesContext';
|
|
2
|
-
import {
|
|
2
|
+
import { useClientLayoutEffect } from '@ainias42/react-bootstrap-mobile';
|
|
3
|
+
import { useContext, useRef } from 'react';
|
|
3
4
|
import { useQueriesStore } from '@/QueryContext/useQueriesStore';
|
|
4
5
|
|
|
5
6
|
export function useQueriesSubscribeToReload(keys: string[], isFetching: boolean, error: any) {
|
|
6
7
|
const contextId = useContext(QueriesContext);
|
|
7
|
-
const setItem = useQueriesStore(s => s.setItem);
|
|
8
|
-
const removeItem = useQueriesStore(s => s.removeItem);
|
|
8
|
+
const setItem = useQueriesStore((s) => s.setItem);
|
|
9
|
+
const removeItem = useQueriesStore((s) => s.removeItem);
|
|
9
10
|
const id = useRef<number | undefined>();
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
useClientLayoutEffect(() => {
|
|
12
13
|
id.current = setItem(contextId, id.current, {
|
|
13
14
|
keys,
|
|
14
15
|
error,
|
|
15
|
-
isFetching
|
|
16
|
+
isFetching,
|
|
16
17
|
});
|
|
17
18
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
18
19
|
}, [contextId, error, isFetching, setItem, ...keys]);
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
useClientLayoutEffect(() => {
|
|
21
22
|
return () => removeItem(contextId, id.current);
|
|
22
23
|
}, [contextId, removeItem]);
|
|
23
24
|
}
|
|
@@ -5,5 +5,5 @@ import { useQueriesStore } from '@/QueryContext/useQueriesStore';
|
|
|
5
5
|
export function useScopedQueriesIsFetching(scope: { global: boolean; subContexts: boolean }, contextId?: number) {
|
|
6
6
|
const context = useContext(QueriesContext);
|
|
7
7
|
|
|
8
|
-
return useQueriesStore(s => s.isFetchingByScope(contextId ?? context, scope));
|
|
8
|
+
return useQueriesStore((s) => s.isFetchingByScope(contextId ?? context, scope));
|
|
9
9
|
}
|
package/src/generateIdsQuery.ts
CHANGED
|
@@ -3,39 +3,39 @@ import { NotFoundError } from '@/NotFoundError';
|
|
|
3
3
|
import { create, windowScheduler } from '@yornaath/batshit';
|
|
4
4
|
import { createInfiniteQuery, createQuery } from 'react-query-kit';
|
|
5
5
|
import { generateQuery } from '@/generateQuery';
|
|
6
|
-
import { hashKey, useQueries } from '@tanstack/react-query';
|
|
6
|
+
import { hashKey, useQueries, useQueryClient } from '@tanstack/react-query';
|
|
7
7
|
import { hoistReactQueryKitAttributes } from '@/hoistReactQueryKitAttributes';
|
|
8
8
|
import { useIndexedData } from '@/hooks/useIndexedData';
|
|
9
9
|
import { useMemo, useRef } from 'react';
|
|
10
10
|
import { useQueriesSubscribeToReload } from '@/QueryContext/useQuerySubscribeToReload';
|
|
11
|
-
import type {
|
|
11
|
+
import type { Awaitable, Override, URecord } from '@ainias42/js-helper';
|
|
12
|
+
import type { CreateInfiniteQueryOptions, CreateQueryOptions, QueryHookOptions } from 'react-query-kit';
|
|
12
13
|
import type { QueriesResults } from '@tanstack/react-query/build/legacy/useQueries';
|
|
13
14
|
import type { QueryClient, QueryFunctionContext, UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
|
|
14
|
-
import type { URecord } from '@ainias42/js-helper';
|
|
15
15
|
|
|
16
16
|
export type Unpacked<T> = T extends (infer U)[] ? U : T;
|
|
17
17
|
|
|
18
18
|
type DeepPartial<T> = T extends object
|
|
19
19
|
? {
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
[P in keyof T]?: DeepPartial<T[P]>;
|
|
21
|
+
}
|
|
22
22
|
: T;
|
|
23
23
|
|
|
24
24
|
export function generateIdsQuery<
|
|
25
25
|
IdType extends number | string | symbol,
|
|
26
26
|
Type extends {
|
|
27
27
|
id: IdType;
|
|
28
|
-
}
|
|
28
|
+
},
|
|
29
29
|
>(
|
|
30
30
|
key: string,
|
|
31
31
|
fetcher: (ids: IdType[]) => Promise<Type[]> | Type[],
|
|
32
32
|
additionalQueryOptions?: Omit<CreateQueryOptions<Type, [IdType]>, 'queryKey' | 'fetcher'> & {
|
|
33
33
|
schedulerTime?: number;
|
|
34
|
-
}
|
|
34
|
+
},
|
|
35
35
|
) {
|
|
36
36
|
const batch = create<URecord<IdType, Type>[], IdType, Type>({
|
|
37
|
-
fetcher: async ids => {
|
|
38
|
-
return [ArrayHelper.arrayToObject(await fetcher(ids), value => value.id)];
|
|
37
|
+
fetcher: async (ids) => {
|
|
38
|
+
return [ArrayHelper.arrayToObject(await fetcher(ids), (value) => value.id)];
|
|
39
39
|
},
|
|
40
40
|
resolver: ([items], id) => {
|
|
41
41
|
const item = items?.[id];
|
|
@@ -44,13 +44,13 @@ export function generateIdsQuery<
|
|
|
44
44
|
}
|
|
45
45
|
return item;
|
|
46
46
|
},
|
|
47
|
-
scheduler: windowScheduler(additionalQueryOptions?.schedulerTime ?? 15)
|
|
47
|
+
scheduler: windowScheduler(additionalQueryOptions?.schedulerTime ?? 15),
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
const useQueryHook = createQuery<Type, [IdType]>({
|
|
51
51
|
queryKey: [key],
|
|
52
|
-
fetcher: identifier => batch.fetch(identifier[0]),
|
|
53
|
-
...additionalQueryOptions
|
|
52
|
+
fetcher: (identifier) => batch.fetch(identifier[0]),
|
|
53
|
+
...additionalQueryOptions,
|
|
54
54
|
});
|
|
55
55
|
|
|
56
56
|
function useManyHook(
|
|
@@ -58,16 +58,16 @@ export function generateIdsQuery<
|
|
|
58
58
|
otherAdditionalQueryOptions: Omit<
|
|
59
59
|
UseQueryOptions<Type, unknown, Type, [string, [IdType]]>,
|
|
60
60
|
'context' | 'fetcher' | 'queryKey'
|
|
61
|
-
> & { reloadWithContext?: boolean } = {}
|
|
61
|
+
> & { reloadWithContext?: boolean } = {},
|
|
62
62
|
) {
|
|
63
|
-
const queries = ids.map(id => ({
|
|
63
|
+
const queries = ids.map((id) => ({
|
|
64
64
|
...useQueryHook.getOptions([id]),
|
|
65
|
-
...otherAdditionalQueryOptions
|
|
65
|
+
...otherAdditionalQueryOptions,
|
|
66
66
|
}));
|
|
67
67
|
|
|
68
68
|
const results = useQueries<Type[], QueriesResults<Type[]>>({
|
|
69
69
|
// @ts-expect-error wrong type in tanstack-query
|
|
70
|
-
queries
|
|
70
|
+
queries,
|
|
71
71
|
}) as UseQueryResult<Type, Error>[];
|
|
72
72
|
|
|
73
73
|
const oldData = useRef<typeof results>([]);
|
|
@@ -84,24 +84,32 @@ export function generateIdsQuery<
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
const shouldReload = otherAdditionalQueryOptions.reloadWithContext ?? true;
|
|
87
|
-
const keys = shouldReload ? ids.map(id => hashKey(useQueryHook.getKey([id as DeepPartial<IdType>]))) : [];
|
|
87
|
+
const keys = shouldReload ? ids.map((id) => hashKey(useQueryHook.getKey([id as DeepPartial<IdType>]))) : [];
|
|
88
88
|
|
|
89
89
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
90
|
-
const isFetching = useMemo(() => oldData.current.some(item => item.isFetching), [oldData.current]);
|
|
90
|
+
const isFetching = useMemo(() => oldData.current.some((item) => item.isFetching), [oldData.current]);
|
|
91
91
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
92
|
-
const error = useMemo(() => oldData.current.find(item => item.error)?.error, [oldData.current]);
|
|
92
|
+
const error = useMemo(() => oldData.current.find((item) => item.error)?.error, [oldData.current]);
|
|
93
93
|
|
|
94
94
|
useQueriesSubscribeToReload(keys, isFetching, error);
|
|
95
95
|
|
|
96
96
|
return returnData.current;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
function useOneHook(
|
|
100
|
-
|
|
99
|
+
function useOneHook(
|
|
100
|
+
identifier: IdType,
|
|
101
|
+
otherAdditionalQueryOptions: Omit<
|
|
102
|
+
QueryHookOptions<Type, unknown, Type, [IdType]>,
|
|
103
|
+
'context' | 'fetcher' | 'queryKey' | 'variables'
|
|
104
|
+
> & { reloadWithContext?: boolean } = {},
|
|
105
|
+
) {
|
|
106
|
+
const result = useQueryHook({ ...otherAdditionalQueryOptions, variables: [identifier] });
|
|
107
|
+
|
|
108
|
+
const shouldReload = otherAdditionalQueryOptions.reloadWithContext ?? true;
|
|
101
109
|
useQueriesSubscribeToReload(
|
|
102
|
-
[hashKey(useQueryHook.getKey([identifier as DeepPartial<IdType>]))],
|
|
110
|
+
shouldReload ? [hashKey(useQueryHook.getKey([identifier as DeepPartial<IdType>]))] : [],
|
|
103
111
|
result.isFetching,
|
|
104
|
-
result.error
|
|
112
|
+
result.error,
|
|
105
113
|
);
|
|
106
114
|
return result;
|
|
107
115
|
}
|
|
@@ -111,29 +119,45 @@ export function generateIdsQuery<
|
|
|
111
119
|
FuncArguments extends Parameters<Func>['length'] extends 0
|
|
112
120
|
? void
|
|
113
121
|
: Parameters<Func> = Parameters<Func>['length'] extends 0 ? void : Parameters<Func>,
|
|
114
|
-
ResultAsArray extends boolean = false
|
|
122
|
+
ResultAsArray extends boolean = false,
|
|
115
123
|
>(
|
|
116
|
-
queryClient: QueryClient,
|
|
117
124
|
filterFunc: Func,
|
|
118
125
|
additionalOptions?: Omit<CreateQueryOptions<IdType[], [FuncArguments]>, 'queryKey' | 'fetcher'> & {
|
|
119
126
|
resultAsArray?: ResultAsArray;
|
|
120
127
|
subKey?: string;
|
|
121
|
-
}
|
|
128
|
+
},
|
|
122
129
|
) {
|
|
130
|
+
let queryClient: QueryClient | undefined;
|
|
131
|
+
|
|
132
|
+
function getQueryClient() {
|
|
133
|
+
if (!queryClient) {
|
|
134
|
+
throw new Error('QueryClient not set');
|
|
135
|
+
}
|
|
136
|
+
return queryClient;
|
|
137
|
+
}
|
|
138
|
+
|
|
123
139
|
const queryHook = generateQuery(
|
|
124
140
|
`${key}-filter${additionalOptions?.subKey ? `-${additionalOptions.subKey}` : ''}`,
|
|
125
141
|
async (filter: FuncArguments) => {
|
|
126
142
|
const result = await filterFunc(filter);
|
|
127
|
-
return result.map(item => {
|
|
128
|
-
|
|
143
|
+
return result.map((item) => {
|
|
144
|
+
getQueryClient().setQueryData(useQueryHook.getKey([item.id as DeepPartial<IdType>]), item);
|
|
129
145
|
return item.id;
|
|
130
146
|
});
|
|
131
147
|
},
|
|
132
148
|
{
|
|
133
149
|
initialDataUpdatedAt: 0,
|
|
134
150
|
...additionalOptions,
|
|
135
|
-
initialData: (additionalOptions?.initialData as IdType[]) ?? []
|
|
136
|
-
|
|
151
|
+
initialData: (additionalOptions?.initialData as IdType[]) ?? [],
|
|
152
|
+
use: [
|
|
153
|
+
function myTestMiddleware(useQueryNext) {
|
|
154
|
+
return (options2, currentQueryClient) => {
|
|
155
|
+
queryClient = useQueryClient(currentQueryClient);
|
|
156
|
+
return useQueryNext(options2, queryClient);
|
|
157
|
+
};
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
},
|
|
137
161
|
);
|
|
138
162
|
|
|
139
163
|
type QueryHookArgument = Parameters<typeof queryHook>[0];
|
|
@@ -142,12 +166,12 @@ export function generateIdsQuery<
|
|
|
142
166
|
function useFilter<Args extends HookArgs>(
|
|
143
167
|
...args: Args
|
|
144
168
|
): ResultAsArray extends true ? UseQueryResult<Type, Error>[] : URecord<IdType, UseQueryResult<Type, Error>> {
|
|
145
|
-
const result = queryHook({variables: args} as QueryHookArgument);
|
|
169
|
+
const result = queryHook({ variables: args } as QueryHookArgument);
|
|
146
170
|
|
|
147
171
|
useQueriesSubscribeToReload(
|
|
148
172
|
[hashKey(queryHook.getKey(args as Parameters<(typeof queryHook)['getKey']>[0]))],
|
|
149
173
|
result.isFetching,
|
|
150
|
-
result.error
|
|
174
|
+
result.error,
|
|
151
175
|
);
|
|
152
176
|
|
|
153
177
|
// When staleTime is same as filterStaleTime, then the queries will run at the same time as the filter
|
|
@@ -155,10 +179,10 @@ export function generateIdsQuery<
|
|
|
155
179
|
// filter will be saved inside the queryClient, so the queries will be updated with the new data.
|
|
156
180
|
// Same goes for reloading with context.
|
|
157
181
|
// Other usages of useManyHook are not affected by this.
|
|
158
|
-
const indexedResult = useManyHook(result.data, {staleTime: Infinity, reloadWithContext: false});
|
|
182
|
+
const indexedResult = useManyHook(result.data, { staleTime: Infinity, reloadWithContext: false });
|
|
159
183
|
const arrayResult: UseQueryResult<Type, Error>[] = useMemo(
|
|
160
|
-
() => ArrayHelper.noUndefined(result.data.map(id => indexedResult[id])),
|
|
161
|
-
[indexedResult, result.data]
|
|
184
|
+
() => ArrayHelper.noUndefined(result.data.map((id) => indexedResult[id])),
|
|
185
|
+
[indexedResult, result.data],
|
|
162
186
|
);
|
|
163
187
|
|
|
164
188
|
if (additionalOptions?.resultAsArray) {
|
|
@@ -169,118 +193,212 @@ export function generateIdsQuery<
|
|
|
169
193
|
return indexedResult as ResultAsArray extends true
|
|
170
194
|
? UseQueryResult<Type, Error>[]
|
|
171
195
|
: URecord<IdType, UseQueryResult<Type, Error>>;
|
|
172
|
-
|
|
173
196
|
}
|
|
174
197
|
|
|
175
198
|
return hoistReactQueryKitAttributes(queryHook, useFilter);
|
|
176
199
|
}
|
|
177
200
|
|
|
178
|
-
function generateInfiniteQuery<PageParams, FuncArguments extends any[]>(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
) =>
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
201
|
+
function generateInfiniteQuery<PageParams, Meta, FuncArguments extends any[] = []>({
|
|
202
|
+
queryFn,
|
|
203
|
+
...options
|
|
204
|
+
}: Omit<
|
|
205
|
+
CreateInfiniteQueryOptions<{ data: IdType[]; meta: Meta }, FuncArguments, Error, PageParams>,
|
|
206
|
+
'getNextPageParam' | 'getPreviousPageParam' | 'queryKey' | 'fetcher'
|
|
207
|
+
> & {
|
|
208
|
+
queryFn: (
|
|
209
|
+
pageParams: PageParams,
|
|
210
|
+
args: FuncArguments,
|
|
211
|
+
context: QueryFunctionContext<[string, FuncArguments], PageParams>,
|
|
212
|
+
) => Awaitable<{ data: Type[]; meta: Meta }>;
|
|
213
|
+
getNextPageParam: (options: {
|
|
214
|
+
getLastPage: () => Type[];
|
|
215
|
+
getAllPages: () => Type[][];
|
|
216
|
+
lastPageParam: PageParams;
|
|
217
|
+
allPageParams: PageParams[];
|
|
218
|
+
lastPageLength: number;
|
|
219
|
+
lastPageMeta: Meta;
|
|
220
|
+
}) => PageParams | undefined;
|
|
221
|
+
getPreviousPageParam?: (options: {
|
|
222
|
+
getFirstPage: () => Type[];
|
|
223
|
+
getAllPages: () => Type[][];
|
|
224
|
+
firstPageParam: PageParams;
|
|
225
|
+
allPageParams: PageParams[];
|
|
226
|
+
firstPageLength: number;
|
|
227
|
+
firstPageMeta: Meta;
|
|
228
|
+
}) => PageParams | undefined;
|
|
229
|
+
}) {
|
|
230
|
+
let queryClient: QueryClient | undefined;
|
|
231
|
+
|
|
232
|
+
function getQueryClient() {
|
|
233
|
+
if (!queryClient) {
|
|
234
|
+
throw new Error('QueryClient not set');
|
|
235
|
+
}
|
|
236
|
+
return queryClient;
|
|
206
237
|
}
|
|
207
|
-
|
|
238
|
+
|
|
208
239
|
function findEntity(id: IdType) {
|
|
209
|
-
return
|
|
240
|
+
return getQueryClient().getQueryData(useQueryHook.getKey([id as DeepPartial<IdType>])) as Type | undefined;
|
|
210
241
|
}
|
|
211
242
|
|
|
212
|
-
const {getPreviousPageParam} = options;
|
|
213
|
-
const infiniteQueryHook = createInfiniteQuery<
|
|
243
|
+
const { getPreviousPageParam } = options;
|
|
244
|
+
const infiniteQueryHook = createInfiniteQuery<
|
|
245
|
+
{
|
|
246
|
+
data: IdType[];
|
|
247
|
+
meta: Meta;
|
|
248
|
+
},
|
|
249
|
+
FuncArguments,
|
|
250
|
+
Error,
|
|
251
|
+
PageParams
|
|
252
|
+
>({
|
|
214
253
|
...options,
|
|
254
|
+
use: [
|
|
255
|
+
function myTestMiddleware(useQueryNext) {
|
|
256
|
+
return (options2, currentQueryClient) => {
|
|
257
|
+
queryClient = useQueryClient(currentQueryClient);
|
|
258
|
+
return useQueryNext(options2, queryClient);
|
|
259
|
+
};
|
|
260
|
+
},
|
|
261
|
+
],
|
|
215
262
|
queryKey: [`${key}-infinite-query`],
|
|
216
263
|
fetcher: async (_, ctx) => {
|
|
217
264
|
const context = ctx as typeof ctx & { pageParam: PageParams };
|
|
218
265
|
const result = await queryFn(
|
|
219
266
|
context.pageParam,
|
|
220
267
|
context.queryKey[1] as FuncArguments,
|
|
221
|
-
context as QueryFunctionContext<[string, FuncArguments], PageParams
|
|
268
|
+
context as QueryFunctionContext<[string, FuncArguments], PageParams>,
|
|
222
269
|
);
|
|
223
|
-
return
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
270
|
+
return {
|
|
271
|
+
meta: result.meta,
|
|
272
|
+
data: result.data.map((item) => {
|
|
273
|
+
getQueryClient().setQueryData(useQueryHook.getKey([item.id as DeepPartial<IdType>]), item);
|
|
274
|
+
return item.id;
|
|
275
|
+
}),
|
|
276
|
+
};
|
|
227
277
|
},
|
|
228
|
-
getNextPageParam: (
|
|
278
|
+
getNextPageParam: (
|
|
279
|
+
lastPage: { meta: Meta; data: IdType[] },
|
|
280
|
+
allPages: {
|
|
281
|
+
meta: Meta;
|
|
282
|
+
data: IdType[];
|
|
283
|
+
}[],
|
|
284
|
+
lastPageParam,
|
|
285
|
+
allPageParams,
|
|
286
|
+
) => {
|
|
229
287
|
return options.getNextPageParam({
|
|
230
|
-
|
|
231
|
-
|
|
288
|
+
lastPageMeta: lastPage.meta,
|
|
289
|
+
getLastPage: () => ArrayHelper.noUndefined(lastPage.data.map(findEntity)),
|
|
290
|
+
getAllPages: () => allPages.map((page) => ArrayHelper.noUndefined(page.data.map(findEntity))),
|
|
232
291
|
lastPageParam,
|
|
233
292
|
allPageParams,
|
|
234
|
-
lastPageLength: lastPage.length
|
|
293
|
+
lastPageLength: lastPage.data.length,
|
|
235
294
|
});
|
|
236
295
|
},
|
|
237
296
|
getPreviousPageParam: !getPreviousPageParam
|
|
238
297
|
? undefined
|
|
239
298
|
: (firstPage, allPages, firstPageParam, allPageParams) => {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
299
|
+
return getPreviousPageParam({
|
|
300
|
+
firstPageMeta: firstPage.meta,
|
|
301
|
+
getFirstPage: () => ArrayHelper.noUndefined(firstPage.data.map(findEntity)),
|
|
302
|
+
getAllPages: () => allPages.map((page) => ArrayHelper.noUndefined(page.data.map(findEntity))),
|
|
303
|
+
firstPageParam,
|
|
304
|
+
allPageParams,
|
|
305
|
+
firstPageLength: firstPage.data.length,
|
|
306
|
+
});
|
|
307
|
+
},
|
|
248
308
|
});
|
|
249
309
|
|
|
250
|
-
|
|
251
|
-
|
|
310
|
+
type HookOptions = Override<
|
|
311
|
+
Parameters<typeof infiniteQueryHook>[0] & {
|
|
312
|
+
select?: undefined;
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
initialData?: {
|
|
316
|
+
pages: { data: Type[]; meta: Meta }[];
|
|
317
|
+
pageParams: PageParams[];
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
>;
|
|
321
|
+
|
|
322
|
+
type InfiniteQueryArgs = FuncArguments extends
|
|
323
|
+
| {
|
|
324
|
+
length: 0;
|
|
325
|
+
}
|
|
326
|
+
| symbol
|
|
327
|
+
| undefined
|
|
328
|
+
? [HookOptions] | []
|
|
329
|
+
: [HookOptions];
|
|
330
|
+
|
|
331
|
+
function useInfiniteQuery(...args: InfiniteQueryArgs) {
|
|
332
|
+
const infiniteQueryClient = useQueryClient();
|
|
333
|
+
|
|
334
|
+
let infiniteOptions: Parameters<typeof infiniteQueryHook>[0] & {
|
|
335
|
+
select?: undefined;
|
|
336
|
+
};
|
|
337
|
+
if (args.length === 1 && 'initialData' in args[0] && args[0].initialData) {
|
|
338
|
+
const { initialData } = args[0];
|
|
339
|
+
infiniteOptions = {
|
|
340
|
+
...args[0],
|
|
341
|
+
initialData: {
|
|
342
|
+
pages: initialData.pages.map((page) => ({
|
|
343
|
+
...page,
|
|
344
|
+
data: page.data.map((item) => {
|
|
345
|
+
infiniteQueryClient.setQueryData(
|
|
346
|
+
useQueryHook.getKey([item.id as DeepPartial<IdType>]),
|
|
347
|
+
item,
|
|
348
|
+
);
|
|
349
|
+
return item.id;
|
|
350
|
+
}),
|
|
351
|
+
})),
|
|
352
|
+
pageParams: initialData.pageParams,
|
|
353
|
+
},
|
|
354
|
+
} as Parameters<typeof infiniteQueryHook>[0] & {
|
|
355
|
+
select?: undefined;
|
|
356
|
+
};
|
|
357
|
+
} else {
|
|
358
|
+
infiniteOptions =
|
|
359
|
+
(args[0] as Parameters<typeof infiniteQueryHook>[0] & {
|
|
360
|
+
select?: undefined;
|
|
361
|
+
}) ?? {};
|
|
362
|
+
}
|
|
363
|
+
const result = infiniteQueryHook(infiniteOptions);
|
|
252
364
|
useQueriesSubscribeToReload(
|
|
253
|
-
[
|
|
365
|
+
[
|
|
366
|
+
hashKey(
|
|
367
|
+
infiniteQueryHook.getKey(
|
|
368
|
+
infiniteOptions.variables as Parameters<(typeof infiniteQueryHook)['getKey']>[0],
|
|
369
|
+
),
|
|
370
|
+
),
|
|
371
|
+
],
|
|
254
372
|
result.isFetching,
|
|
255
|
-
result.error
|
|
373
|
+
result.error,
|
|
256
374
|
);
|
|
257
|
-
const ids = useMemo(() => result.data?.pages.
|
|
375
|
+
const ids = useMemo(() => result.data?.pages.flatMap((page) => page.data) ?? [], [result.data?.pages]);
|
|
258
376
|
|
|
259
377
|
// When staleTime is same as filterStaleTime, then the queries will run at the same time as the filter
|
|
260
378
|
// Because of this, use staleTime: Inifinity to prevent the queries from running again. The result of the
|
|
261
379
|
// filter will be saved inside the queryClient, so the queries will be updated with the new data.
|
|
262
380
|
// Same goes for reloading with context.
|
|
263
381
|
// Other usages of useManyHook are not affected by this.
|
|
264
|
-
const manyData = useManyHook(ids, {staleTime: Infinity, reloadWithContext: false});
|
|
382
|
+
const manyData = useManyHook(ids, { staleTime: Infinity, reloadWithContext: false });
|
|
265
383
|
const indexedData = useIndexedData(manyData);
|
|
266
384
|
|
|
267
385
|
const pages = useMemo(() => {
|
|
268
386
|
return result.data
|
|
269
|
-
? result.data.pages.map(page => ArrayHelper.noUndefined(page.map(id => indexedData[id])))
|
|
387
|
+
? result.data.pages.map((page) => ArrayHelper.noUndefined(page.data.map((id) => indexedData[id])))
|
|
270
388
|
: [];
|
|
271
389
|
}, [indexedData, result.data]);
|
|
272
390
|
|
|
273
391
|
return {
|
|
274
392
|
...result,
|
|
275
|
-
data: !result.data ? undefined : {...result.data, pages}
|
|
393
|
+
data: !result.data ? undefined : { ...result.data, pages },
|
|
276
394
|
};
|
|
277
395
|
}
|
|
278
396
|
|
|
279
|
-
// @ts-expect-error type
|
|
397
|
+
// @ts-expect-error wrong type in tanstack-query
|
|
280
398
|
return hoistReactQueryKitAttributes(infiniteQueryHook, useInfiniteQuery);
|
|
281
399
|
}
|
|
282
400
|
|
|
283
401
|
const useMany = hoistReactQueryKitAttributes(useQueryHook, useManyHook);
|
|
284
402
|
const useOne = hoistReactQueryKitAttributes(useQueryHook, useOneHook);
|
|
285
|
-
return {useMany, useOne, generateFilter, generateInfiniteQuery};
|
|
403
|
+
return { useMany, useOne, generateFilter, generateInfiniteQuery };
|
|
286
404
|
}
|