@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.
Files changed (58) hide show
  1. package/.eslintrc.cjs +2 -1
  2. package/.prettierrc +1 -1
  3. package/dist/QueryContext/QueriesContextProvider.d.ts +1 -1
  4. package/dist/QueryContext/QueriesContextProvider.js +1 -1
  5. package/dist/QueryContext/QueriesContextProvider.js.map +1 -1
  6. package/dist/QueryContext/SubQueriesContext.d.ts +1 -1
  7. package/dist/QueryContext/SubQueriesContext.js +5 -5
  8. package/dist/QueryContext/SubQueriesContext.js.map +1 -1
  9. package/dist/QueryContext/useGenerateQueriesContextId.js +1 -1
  10. package/dist/QueryContext/useGenerateQueriesContextId.js.map +1 -1
  11. package/dist/QueryContext/useQueriesError.js +1 -1
  12. package/dist/QueryContext/useQueriesError.js.map +1 -1
  13. package/dist/QueryContext/useQueriesIsFetching.js +1 -1
  14. package/dist/QueryContext/useQueriesIsFetching.js.map +1 -1
  15. package/dist/QueryContext/useQueriesReload.js +1 -1
  16. package/dist/QueryContext/useQueriesReload.js.map +1 -1
  17. package/dist/QueryContext/useQueriesStore.js +17 -17
  18. package/dist/QueryContext/useQueriesStore.js.map +1 -1
  19. package/dist/QueryContext/useQuerySubscribeToReload.js +7 -6
  20. package/dist/QueryContext/useQuerySubscribeToReload.js.map +1 -1
  21. package/dist/QueryContext/useScopedQueriesIsFetching.js +1 -1
  22. package/dist/QueryContext/useScopedQueriesIsFetching.js.map +1 -1
  23. package/dist/generateIdsQuery.d.ts +129 -23
  24. package/dist/generateIdsQuery.js +89 -36
  25. package/dist/generateIdsQuery.js.map +1 -1
  26. package/dist/generateNestedIdQuery.d.ts +126 -21
  27. package/dist/generateNestedIdQuery.js +2 -2
  28. package/dist/generateNestedIdQuery.js.map +1 -1
  29. package/dist/generateQuery.js +1 -1
  30. package/dist/generateQuery.js.map +1 -1
  31. package/dist/hooks/formatInitialPage.d.ts +10 -0
  32. package/dist/hooks/formatInitialPage.js +13 -0
  33. package/dist/hooks/formatInitialPage.js.map +1 -0
  34. package/dist/hooks/useArrayData.js +1 -1
  35. package/dist/hooks/useArrayData.js.map +1 -1
  36. package/dist/index.d.ts +1 -0
  37. package/dist/index.js +1 -0
  38. package/dist/index.js.map +1 -1
  39. package/dist/tsconfig.tsbuildinfo +1 -1
  40. package/package.json +6 -2
  41. package/src/QueryContext/QueriesContextProvider.tsx +2 -2
  42. package/src/QueryContext/SubQueriesContext.tsx +7 -7
  43. package/src/QueryContext/useGenerateQueriesContextId.ts +1 -1
  44. package/src/QueryContext/useQueriesError.ts +1 -1
  45. package/src/QueryContext/useQueriesIsFetching.ts +1 -1
  46. package/src/QueryContext/useQueriesReload.ts +1 -1
  47. package/src/QueryContext/useQueriesStore.ts +21 -21
  48. package/src/QueryContext/useQuerySubscribeToReload.ts +7 -6
  49. package/src/QueryContext/useScopedQueriesIsFetching.ts +1 -1
  50. package/src/generateIdsQuery.ts +213 -95
  51. package/src/generateNestedIdQuery.ts +4 -4
  52. package/src/generateQuery.ts +12 -13
  53. package/src/hoistReactQueryKitAttributes.ts +1 -1
  54. package/src/hooks/formatInitialPage.ts +12 -0
  55. package/src/hooks/useArrayData.ts +3 -3
  56. package/src/hooks/useIndexedData.ts +3 -3
  57. package/src/index.ts +1 -0
  58. 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 { useContext, useLayoutEffect, useRef } from 'react';
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
- useLayoutEffect(() => {
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
- useLayoutEffect(() => {
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
  }
@@ -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 { CreateInfiniteQueryOptions, CreateQueryOptions } from 'react-query-kit';
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
- [P in keyof T]?: DeepPartial<T[P]>;
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(identifier: IdType) {
100
- const result = useQueryHook({variables: [identifier]});
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
- queryClient.setQueryData(useQueryHook.getKey([item.id as DeepPartial<IdType>]), item);
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
- queryClient: QueryClient,
180
- {
181
- queryFn,
182
- ...options
183
- }: Omit<
184
- CreateInfiniteQueryOptions<IdType[], FuncArguments, Error, PageParams>,
185
- 'getNextPageParam' | 'getPreviousPageParam' | 'queryKey' | 'fetcher'
186
- > & {
187
- queryFn: (
188
- pageParams: PageParams,
189
- args: FuncArguments,
190
- context: QueryFunctionContext<[string, FuncArguments], PageParams>
191
- ) => Promise<Type[]> | Type[];
192
- getNextPageParam: (options: {
193
- getLastPage: () => Type[];
194
- getAllPages: () => Type[][];
195
- lastPageParam: PageParams;
196
- allPageParams: PageParams[];
197
- lastPageLength: number;
198
- }) => PageParams | undefined;
199
- getPreviousPageParam?: (options: {
200
- getFirstPage: () => Type[];
201
- getAllPages: () => Type[][];
202
- firstPageParam: PageParams;
203
- allPageParams: PageParams[];
204
- firstPageLength: number;
205
- }) => PageParams | undefined;
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 queryClient.getQueryData(useQueryHook.getKey([id as DeepPartial<IdType>])) as Type | undefined;
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<IdType[], FuncArguments, Error, PageParams>({
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 result.map(item => {
224
- queryClient.setQueryData(useQueryHook.getKey([item.id as DeepPartial<IdType>]), item);
225
- return item.id;
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: (lastPage: IdType[], allPages: IdType[][], lastPageParam, allPageParams) => {
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
- getLastPage: () => ArrayHelper.noUndefined(lastPage.map(findEntity)),
231
- getAllPages: () => allPages.map(page => ArrayHelper.noUndefined(page.map(findEntity))),
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
- return getPreviousPageParam({
241
- getFirstPage: () => ArrayHelper.noUndefined(firstPage.map(findEntity)),
242
- getAllPages: () => allPages.map(page => ArrayHelper.noUndefined(page.map(findEntity))),
243
- firstPageParam,
244
- allPageParams,
245
- firstPageLength: firstPage.length
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
- function useInfiniteQuery(...args: FuncArguments) {
251
- const result = infiniteQueryHook({variables: args});
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
- [hashKey(infiniteQueryHook.getKey(args as Parameters<(typeof infiniteQueryHook)['getKey']>[0]))],
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.flat() ?? [], [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 is okay
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
  }