@ventlio/tanstack-query 0.2.72 → 0.2.74

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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- ### This is not a replacement for @tanstack/react-query
1
+ ### This is a complementary library that should be used with @tanstack/react-query for API REQUESTS and more
2
2
 
3
3
  ## WHY THIS PACKAGE?
4
4
 
@@ -12,12 +12,27 @@ But we were not discouraged. So, we set out to find a solution which led to the
12
12
 
13
13
  > Please note that this package is still being developed and may not function as expected. We are working to refine its implementation structure to meet a functional standard. The documentation may not align with the current implementation, so if you encounter any difficulties while setting up the package, please raise an issue in the GitHub repository. We appreciate your patience and understanding as we work to improve this package.
14
14
 
15
- ## Install
15
+ ### Tasks
16
+
17
+ - [✅] Global settings for requests
18
+ - [✅] Requests context implementations
19
+ - [✅] Post, Get, Patch Requests
20
+ - [✅] Query key tracker to track dynamic query and help fetch query cache from any page
21
+ - [✅] Persistent queries implementation (Not completed)
22
+ - [❌] Put request
23
+ - [❌] Generic return type (this is currently an issue if the API does not return object with the necessary properties required by the library)
24
+ - [❌] Generic Pagination for any response without infinite queries
25
+ - [❌] Infinite Get Query implementation (still using implementation meant for our use case)
26
+ - [❌] Server sent events
27
+ - [❌] Socket implementations
28
+ - [❌] Tests
29
+
30
+ ## Installation
16
31
 
17
32
  > You must install @tanstack/react-query and axios first to use this package
18
33
 
19
34
  ```
20
- npm install @tanstack/react-query axios
35
+ yarn add @tanstack/react-query axios
21
36
  ```
22
37
 
23
38
  After that install this package
@@ -26,16 +41,41 @@ After that install this package
26
41
  $ npm install @ventlio/tanstack-query
27
42
  ```
28
43
 
44
+ OR
45
+
29
46
  ```
30
47
  $ yarn add @ventlio/tanstack-query
31
48
  ```
32
49
 
50
+ ## CURRENT RETURN TYPE
51
+
52
+ Currently the library return type expects data structure of the below schema, so depending on the API design,
53
+ you can reach out to the developer to implement the return type that follows the below schema.
54
+
55
+ ```js
56
+ export interface IRequestError {
57
+ statusCode: number;
58
+ message: string;
59
+ timeStamp: Date;
60
+ status: boolean;
61
+ data?: any;
62
+ }
63
+
64
+ export interface IRequestSuccess<T> {
65
+ statusCode: number;
66
+ message: string;
67
+ timeStamp: Date;
68
+ status: boolean;
69
+ data: T;
70
+ }
71
+ ```
33
72
 
34
73
  ## Getting Started
35
74
 
36
75
  Follow the below instructions to have the package running on your project
37
76
 
38
77
  ### Set the environment variables
78
+
39
79
  ```env
40
80
  # For ReactJS
41
81
  REACT_APP_API_URL='https://api.example.com'
@@ -46,6 +86,7 @@ NEXT_PUBLIC_API_URL='https://api.example.com'
46
86
  NEXT_PUBLIC_API_TIMEOUT=300000
47
87
 
48
88
  ```
89
+
49
90
  ```js
50
91
  import { QueryClient } from '@tanstack/react-query';
51
92
  import { TanstackQueryConfig, bootstrapQueryRequest } from '@ventlio/tanstack-query';
@@ -56,6 +97,17 @@ const queryClient = new QueryClient();
56
97
  // do this before adding the queryClient to QueryClientProvider
57
98
  bootstrapQueryRequest(queryClient);
58
99
 
100
+ // recommended setup for mobile apps as the .env setup won't work
101
+ bootstrapQueryRequest(queryClient, {
102
+ context: 'app', // this is required to make the library switch to app context where necessary
103
+ environments: {
104
+ appBaseUrl: baseUrl,
105
+ appTimeout: 30000,
106
+ },
107
+ modelConfig: {
108
+ idColumn: 'id', // used for useQueryModel to uniquely identify query data in a collection/array instance
109
+ },
110
+ });
59
111
  ```
60
112
 
61
113
  You can now use it in a QueryClientProvider
@@ -77,19 +129,17 @@ function App() {
77
129
  Updating the configurations inside a component
78
130
 
79
131
  ```jsx
80
- import {
81
- useQueryBaseURL,
82
- useQueryHeaders,
83
- useQueryTimeout,
84
- } from '@ventlio/tanstack-query';
132
+ import { useQueryBaseURL, useQueryHeaders, useQueryTimeout } from '@ventlio/tanstack-query';
85
133
 
86
134
  function LoginPage() {
87
135
  const { headers, setQueryHeaders } = useQueryHeaders();
88
-
136
+ const [authToken, setAuthToken] = useState();
89
137
  useEffect(() => {
90
138
  // after user has logged in successfully set the authorization header token
91
- headers.Authorization = 'Bearer token'; // this will be used for subsequent queries
92
- setQueryHeaders(headers);
139
+ // this should also be done mostly in the layout that contains the authenticated views of the app
140
+ // for instance in AuthLayout, so that after login in the authToken can still be used to authenticate future request
141
+ // when user refreshes the page
142
+ setQueryHeaders({ Authorization: `Bearer ${authToken}` });
93
143
  }, []);
94
144
 
95
145
  return <>{/** codes */}</>;
@@ -133,27 +183,16 @@ The `useGetRequest` hook returns an object with the following properties:
133
183
  import { useGetRequest } from '@ventlio/tanstack-query';
134
184
 
135
185
  const MyComponent = () => {
136
- const {
137
- data,
138
- isLoading,
139
- isError,
140
- error,
141
- updatePath,
142
- nextPage,
143
- prevPage,
144
- get,
145
- gotoPage,
146
- page,
147
- queryKey,
148
- } = useGetRequest({
149
- path: '/api/mydata',
150
- load: true,
151
- queryOptions: {
152
- staleTime: 10000,
153
- refetchOnWindowFocus: false,
154
- },
155
- keyTracker: 'mydata',
156
- });
186
+ const { data, isLoading, isError, error, updatePath, nextPage, prevPage, get, gotoPage, page, queryKey } =
187
+ useGetRequest({
188
+ path: '/api/mydata',
189
+ load: true,
190
+ queryOptions: {
191
+ staleTime: 10000,
192
+ refetchOnWindowFocus: false,
193
+ },
194
+ keyTracker: 'mydata',
195
+ });
157
196
 
158
197
  return (
159
198
  <div>
@@ -419,8 +458,7 @@ import { useKeyTrackerModel } from '@ventlio/tanstack-query';
419
458
  2. Call `useKeyTrackerModel` with a `keyTracker` parameter which is a string that uniquely identifies the query key:
420
459
 
421
460
  ```javascript
422
- const { refetchQuery, getQueryKey, queryKey, data } =
423
- useKeyTrackerModel < MyDataType > 'myKeyTracker';
461
+ const { refetchQuery, getQueryKey, queryKey, data } = useKeyTrackerModel < MyDataType > 'myKeyTracker';
424
462
  ```
425
463
 
426
464
  3. Invoke `getQueryKey` function to retrieve the query key:
@@ -467,8 +505,7 @@ import { useKeyTrackerModel } from '@ventlio/tanstack-query';
467
505
  const MyComponent = () => {
468
506
  const queryClient = useQueryClient();
469
507
 
470
- const { refetchQuery, getQueryKey, queryKey, data } =
471
- useKeyTrackerModel < MyDataType > 'myKeyTracker';
508
+ const { refetchQuery, getQueryKey, queryKey, data } = useKeyTrackerModel < MyDataType > 'myKeyTracker';
472
509
 
473
510
  const handleClick = async () => {
474
511
  // Retrieve the query key
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ export { useKeyTrackerModel } from './model/useKeyTrackerModel.js';
9
9
  export { useQueryModel } from './model/useQueryModel.js';
10
10
  export { useRefetchQuery } from './model/useRefetchQuery.js';
11
11
  export { useDeleteRequest } from './queries/useDeleteRequest.js';
12
+ export { useGetInfiniteRequest } from './queries/useGetInfiniteRequest.js';
12
13
  export { useGetRequest } from './queries/useGetRequest.js';
13
14
  export { usePatchRequest } from './queries/usePatchRequest.js';
14
15
  export { usePostRequest } from './queries/usePostRequest.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;"}
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import 'url-search-params-polyfill';
2
- import { useQueryClient, useQuery, useMutation } from '@tanstack/react-query';
2
+ import { useQueryClient, useQuery, useInfiniteQuery, useMutation } from '@tanstack/react-query';
3
3
  import result from 'lodash.result';
4
4
  import lodashSet from 'lodash.set';
5
5
  import { useState, useMemo, useEffect, startTransition } from 'react';
@@ -396,11 +396,12 @@ const useDeleteRequest = (deleteOptions) => {
396
396
  const [options, setOptions] = useState();
397
397
  const { API_URL, TIMEOUT } = useEnvironmentVariables();
398
398
  const { getHeaders } = useQueryHeaders();
399
- const sendRequest = async (res, rej) => {
399
+ const sendRequest = async (res, rej, queryKey) => {
400
400
  // get request headers
401
401
  const globalHeaders = getHeaders();
402
+ const [url] = (queryKey ?? []);
402
403
  const postResponse = await makeRequest({
403
- path: requestPath,
404
+ path: url ?? requestPath,
404
405
  headers: { ...globalHeaders, ...headers },
405
406
  method: HttpMethod.DELETE,
406
407
  baseURL: baseUrl ?? API_URL,
@@ -413,7 +414,7 @@ const useDeleteRequest = (deleteOptions) => {
413
414
  rej(postResponse);
414
415
  }
415
416
  };
416
- const query = useQuery([requestPath, {}], () => new Promise((res, rej) => sendRequest(res, rej)), { enabled: false, ...options });
417
+ const query = useQuery([requestPath, {}], ({ queryKey }) => new Promise((res, rej) => sendRequest(res, rej, queryKey)), { enabled: false, ...options });
417
418
  const updatedPathAsync = async (link) => {
418
419
  return updateDeletePath(link);
419
420
  };
@@ -431,6 +432,67 @@ const useDeleteRequest = (deleteOptions) => {
431
432
  return { destroy, ...query };
432
433
  };
433
434
 
435
+ const useGetInfiniteRequest = ({ path, load = false, queryOptions, keyTracker, baseUrl, headers, }) => {
436
+ const { API_URL, TIMEOUT } = useEnvironmentVariables();
437
+ const { getHeaders } = useQueryHeaders();
438
+ let queryClient = useQueryClient();
439
+ // eslint-disable-next-line react-hooks/exhaustive-deps
440
+ queryClient = useMemo(() => queryClient, []);
441
+ const sendRequest = async (res, rej, pageParam) => {
442
+ if (load) {
443
+ // get request headers
444
+ const globalHeaders = getHeaders();
445
+ const getResponse = await makeRequest({
446
+ path: pageParam ?? path,
447
+ headers: { ...globalHeaders, ...headers },
448
+ baseURL: baseUrl ?? API_URL,
449
+ timeout: TIMEOUT,
450
+ });
451
+ if (getResponse.status) {
452
+ res(getResponse);
453
+ }
454
+ else {
455
+ rej(getResponse);
456
+ }
457
+ }
458
+ else {
459
+ res(null);
460
+ }
461
+ };
462
+ const query = useInfiniteQuery([path, {}], ({ pageParam = path }) => new Promise((res, rej) => sendRequest(res, rej, pageParam)), {
463
+ enabled: load,
464
+ getNextPageParam: (lastPage, pages) => constructPaginationLink('next_page', lastPage, pages),
465
+ getPreviousPageParam: (lastPage, pages) => constructPaginationLink('previous_page', lastPage, pages),
466
+ ...queryOptions,
467
+ });
468
+ /**
469
+ *
470
+ * This pagination implementation is currently tied to our use case
471
+ */
472
+ const constructPaginationLink = (direction, lastPage, pages) => {
473
+ const [pathname, queryString] = lastPage.split('?');
474
+ const queryParams = new URLSearchParams(queryString);
475
+ const lastPageItem = pages[pages.length - 1];
476
+ queryParams.set('page', String(lastPageItem?.data.pagination[direction]));
477
+ return pathname + '?' + queryParams.toString();
478
+ };
479
+ useEffect(() => {
480
+ if (keyTracker) {
481
+ // set expiration time for the tracker
482
+ queryClient.setQueryDefaults([keyTracker], {
483
+ cacheTime: Infinity,
484
+ staleTime: Infinity,
485
+ });
486
+ queryClient.setQueryData([keyTracker], [path, {}]);
487
+ }
488
+ }, [keyTracker, path, queryClient, queryOptions?.staleTime]);
489
+ const flattenedData = query.data?.pages.flat() ?? [];
490
+ return {
491
+ ...query,
492
+ flattenedData,
493
+ };
494
+ };
495
+
434
496
  const useGetRequest = ({ path, load = false, queryOptions, keyTracker, baseUrl, headers, }) => {
435
497
  const [requestPath, updatePath] = useState(path);
436
498
  const [options, setOptions] = useState(queryOptions);
@@ -440,12 +502,13 @@ const useGetRequest = ({ path, load = false, queryOptions, keyTracker, baseUrl,
440
502
  let queryClient = useQueryClient();
441
503
  // eslint-disable-next-line react-hooks/exhaustive-deps
442
504
  queryClient = useMemo(() => queryClient, []);
443
- const sendRequest = async (res, rej) => {
505
+ const sendRequest = async (res, rej, queryKey) => {
444
506
  if (load) {
445
507
  // get request headers
446
508
  const globalHeaders = getHeaders();
509
+ const [url] = (queryKey ?? []);
447
510
  const getResponse = await makeRequest({
448
- path: requestPath,
511
+ path: url ?? requestPath,
449
512
  headers: { ...globalHeaders, ...headers },
450
513
  baseURL: baseUrl ?? API_URL,
451
514
  timeout: TIMEOUT,
@@ -461,7 +524,7 @@ const useGetRequest = ({ path, load = false, queryOptions, keyTracker, baseUrl,
461
524
  res(null);
462
525
  }
463
526
  };
464
- const query = useQuery([requestPath, {}], () => new Promise((res, rej) => sendRequest(res, rej)), {
527
+ const query = useQuery([requestPath, {}], ({ queryKey }) => new Promise((res, rej) => sendRequest(res, rej, queryKey)), {
465
528
  enabled: load,
466
529
  ...options,
467
530
  });
@@ -624,5 +687,5 @@ const usePostRequest = ({ path, isFormData = false, baseUrl, headers, fileSelect
624
687
  return { post, ...mutation };
625
688
  };
626
689
 
627
- export { ContentType, HttpMethod, axiosInstance, bootstrapQueryRequest, buildFormData, errorTransformer, getDateInFuture, makeRequest, scrollToTop, successTransformer, useDeleteRequest, useEnvironmentVariables, useGetRequest, useKeyTrackerModel, usePatchRequest, usePostRequest, useQueryConfig, useQueryHeaders, useQueryModel, useReactNativeEnv, useRefetchQuery };
690
+ export { ContentType, HttpMethod, axiosInstance, bootstrapQueryRequest, buildFormData, errorTransformer, getDateInFuture, makeRequest, scrollToTop, successTransformer, useDeleteRequest, useEnvironmentVariables, useGetInfiniteRequest, useGetRequest, useKeyTrackerModel, usePatchRequest, usePostRequest, useQueryConfig, useQueryHeaders, useQueryModel, useReactNativeEnv, useRefetchQuery };
628
691
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,5 +1,6 @@
1
1
  export * from './queries.interface';
2
2
  export * from './useDeleteRequest';
3
+ export * from './useGetInfiniteRequest';
3
4
  export * from './useGetRequest';
4
5
  export * from './usePatchRequest';
5
6
  export * from './usePostRequest';
@@ -1,4 +1,4 @@
1
- import type { UseQueryOptions } from '@tanstack/react-query';
1
+ import type { UseInfiniteQueryOptions, UseQueryOptions } from '@tanstack/react-query';
2
2
  import type { RawAxiosRequestHeaders } from 'axios';
3
3
  import type { IRequestError, IRequestSuccess } from '../request';
4
4
  export interface IPagination {
@@ -10,6 +10,7 @@ export interface IPagination {
10
10
  total: number;
11
11
  }
12
12
  export type TanstackQueryOption<TResponse> = UseQueryOptions<IRequestSuccess<TResponse | undefined>, IRequestError, IRequestSuccess<TResponse | undefined>, Array<any>>;
13
+ export type TanstackInfiniteQueryOption<TResponse> = UseInfiniteQueryOptions<IRequestSuccess<TResponse | undefined>, IRequestError, IRequestSuccess<TResponse | undefined>, Array<any>>;
13
14
  export interface DefaultRequestOptions {
14
15
  baseUrl?: string;
15
16
  headers?: RawAxiosRequestHeaders;
@@ -13,11 +13,12 @@ const useDeleteRequest = (deleteOptions) => {
13
13
  const [options, setOptions] = useState();
14
14
  const { API_URL, TIMEOUT } = useEnvironmentVariables();
15
15
  const { getHeaders } = useQueryHeaders();
16
- const sendRequest = async (res, rej) => {
16
+ const sendRequest = async (res, rej, queryKey) => {
17
17
  // get request headers
18
18
  const globalHeaders = getHeaders();
19
+ const [url] = (queryKey ?? []);
19
20
  const postResponse = await makeRequest({
20
- path: requestPath,
21
+ path: url ?? requestPath,
21
22
  headers: { ...globalHeaders, ...headers },
22
23
  method: HttpMethod.DELETE,
23
24
  baseURL: baseUrl ?? API_URL,
@@ -30,7 +31,7 @@ const useDeleteRequest = (deleteOptions) => {
30
31
  rej(postResponse);
31
32
  }
32
33
  };
33
- const query = useQuery([requestPath, {}], () => new Promise((res, rej) => sendRequest(res, rej)), { enabled: false, ...options });
34
+ const query = useQuery([requestPath, {}], ({ queryKey }) => new Promise((res, rej) => sendRequest(res, rej, queryKey)), { enabled: false, ...options });
34
35
  const updatedPathAsync = async (link) => {
35
36
  return updateDeletePath(link);
36
37
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useDeleteRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"useDeleteRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,140 @@
1
+ import type { IRequestSuccess } from '../request';
2
+ import type { DefaultRequestOptions, TanstackInfiniteQueryOption } from './queries.interface';
3
+ export declare const useGetInfiniteRequest: <TResponse extends Record<string, any>>({ path, load, queryOptions, keyTracker, baseUrl, headers, }: {
4
+ path: string;
5
+ load?: boolean | undefined;
6
+ queryOptions?: TanstackInfiniteQueryOption<TResponse> | undefined;
7
+ keyTracker?: string | undefined;
8
+ } & DefaultRequestOptions) => {
9
+ flattenedData: IRequestSuccess<TResponse>[];
10
+ data: undefined;
11
+ error: any;
12
+ isError: true;
13
+ isLoading: false;
14
+ isLoadingError: true;
15
+ isRefetchError: false;
16
+ isSuccess: false;
17
+ status: "error";
18
+ fetchNextPage: (options?: import("@tanstack/react-query").FetchNextPageOptions | undefined) => Promise<import("@tanstack/react-query").InfiniteQueryObserverResult<IRequestSuccess<TResponse>, any>>;
19
+ fetchPreviousPage: (options?: import("@tanstack/react-query").FetchPreviousPageOptions | undefined) => Promise<import("@tanstack/react-query").InfiniteQueryObserverResult<IRequestSuccess<TResponse>, any>>;
20
+ hasNextPage?: boolean | undefined;
21
+ hasPreviousPage?: boolean | undefined;
22
+ isFetchingNextPage: boolean;
23
+ isFetchingPreviousPage: boolean;
24
+ dataUpdatedAt: number;
25
+ errorUpdatedAt: number;
26
+ failureCount: number;
27
+ failureReason: any;
28
+ errorUpdateCount: number;
29
+ isFetched: boolean;
30
+ isFetchedAfterMount: boolean;
31
+ isFetching: boolean;
32
+ isInitialLoading: boolean;
33
+ isPaused: boolean;
34
+ isPlaceholderData: boolean;
35
+ isPreviousData: boolean;
36
+ isRefetching: boolean;
37
+ isStale: boolean;
38
+ refetch: <TPageData>(options?: (import("@tanstack/react-query").RefetchOptions & import("@tanstack/react-query").RefetchQueryFilters<TPageData>) | undefined) => Promise<import("@tanstack/react-query").QueryObserverResult<import("@tanstack/react-query").InfiniteData<IRequestSuccess<TResponse>>, any>>;
39
+ remove: () => void;
40
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
41
+ } | {
42
+ flattenedData: IRequestSuccess<TResponse>[];
43
+ data: undefined;
44
+ error: null;
45
+ isError: false;
46
+ isLoading: true;
47
+ isLoadingError: false;
48
+ isRefetchError: false;
49
+ isSuccess: false;
50
+ status: "loading";
51
+ fetchNextPage: (options?: import("@tanstack/react-query").FetchNextPageOptions | undefined) => Promise<import("@tanstack/react-query").InfiniteQueryObserverResult<IRequestSuccess<TResponse>, any>>;
52
+ fetchPreviousPage: (options?: import("@tanstack/react-query").FetchPreviousPageOptions | undefined) => Promise<import("@tanstack/react-query").InfiniteQueryObserverResult<IRequestSuccess<TResponse>, any>>;
53
+ hasNextPage?: boolean | undefined;
54
+ hasPreviousPage?: boolean | undefined;
55
+ isFetchingNextPage: boolean;
56
+ isFetchingPreviousPage: boolean;
57
+ dataUpdatedAt: number;
58
+ errorUpdatedAt: number;
59
+ failureCount: number;
60
+ failureReason: any;
61
+ errorUpdateCount: number;
62
+ isFetched: boolean;
63
+ isFetchedAfterMount: boolean;
64
+ isFetching: boolean;
65
+ isInitialLoading: boolean;
66
+ isPaused: boolean;
67
+ isPlaceholderData: boolean;
68
+ isPreviousData: boolean;
69
+ isRefetching: boolean;
70
+ isStale: boolean;
71
+ refetch: <TPageData>(options?: (import("@tanstack/react-query").RefetchOptions & import("@tanstack/react-query").RefetchQueryFilters<TPageData>) | undefined) => Promise<import("@tanstack/react-query").QueryObserverResult<import("@tanstack/react-query").InfiniteData<IRequestSuccess<TResponse>>, any>>;
72
+ remove: () => void;
73
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
74
+ } | {
75
+ flattenedData: IRequestSuccess<TResponse>[];
76
+ data: import("@tanstack/react-query").InfiniteData<IRequestSuccess<TResponse>>;
77
+ error: any;
78
+ isError: true;
79
+ isLoading: false;
80
+ isLoadingError: false;
81
+ isRefetchError: true;
82
+ isSuccess: false;
83
+ status: "error";
84
+ fetchNextPage: (options?: import("@tanstack/react-query").FetchNextPageOptions | undefined) => Promise<import("@tanstack/react-query").InfiniteQueryObserverResult<IRequestSuccess<TResponse>, any>>;
85
+ fetchPreviousPage: (options?: import("@tanstack/react-query").FetchPreviousPageOptions | undefined) => Promise<import("@tanstack/react-query").InfiniteQueryObserverResult<IRequestSuccess<TResponse>, any>>;
86
+ hasNextPage?: boolean | undefined;
87
+ hasPreviousPage?: boolean | undefined;
88
+ isFetchingNextPage: boolean;
89
+ isFetchingPreviousPage: boolean;
90
+ dataUpdatedAt: number;
91
+ errorUpdatedAt: number;
92
+ failureCount: number;
93
+ failureReason: any;
94
+ errorUpdateCount: number;
95
+ isFetched: boolean;
96
+ isFetchedAfterMount: boolean;
97
+ isFetching: boolean;
98
+ isInitialLoading: boolean;
99
+ isPaused: boolean;
100
+ isPlaceholderData: boolean;
101
+ isPreviousData: boolean;
102
+ isRefetching: boolean;
103
+ isStale: boolean;
104
+ refetch: <TPageData>(options?: (import("@tanstack/react-query").RefetchOptions & import("@tanstack/react-query").RefetchQueryFilters<TPageData>) | undefined) => Promise<import("@tanstack/react-query").QueryObserverResult<import("@tanstack/react-query").InfiniteData<IRequestSuccess<TResponse>>, any>>;
105
+ remove: () => void;
106
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
107
+ } | {
108
+ flattenedData: IRequestSuccess<TResponse>[];
109
+ data: import("@tanstack/react-query").InfiniteData<IRequestSuccess<TResponse>>;
110
+ error: null;
111
+ isError: false;
112
+ isLoading: false;
113
+ isLoadingError: false;
114
+ isRefetchError: false;
115
+ isSuccess: true;
116
+ status: "success";
117
+ fetchNextPage: (options?: import("@tanstack/react-query").FetchNextPageOptions | undefined) => Promise<import("@tanstack/react-query").InfiniteQueryObserverResult<IRequestSuccess<TResponse>, any>>;
118
+ fetchPreviousPage: (options?: import("@tanstack/react-query").FetchPreviousPageOptions | undefined) => Promise<import("@tanstack/react-query").InfiniteQueryObserverResult<IRequestSuccess<TResponse>, any>>;
119
+ hasNextPage?: boolean | undefined;
120
+ hasPreviousPage?: boolean | undefined;
121
+ isFetchingNextPage: boolean;
122
+ isFetchingPreviousPage: boolean;
123
+ dataUpdatedAt: number;
124
+ errorUpdatedAt: number;
125
+ failureCount: number;
126
+ failureReason: any;
127
+ errorUpdateCount: number;
128
+ isFetched: boolean;
129
+ isFetchedAfterMount: boolean;
130
+ isFetching: boolean;
131
+ isInitialLoading: boolean;
132
+ isPaused: boolean;
133
+ isPlaceholderData: boolean;
134
+ isPreviousData: boolean;
135
+ isRefetching: boolean;
136
+ isStale: boolean;
137
+ refetch: <TPageData>(options?: (import("@tanstack/react-query").RefetchOptions & import("@tanstack/react-query").RefetchQueryFilters<TPageData>) | undefined) => Promise<import("@tanstack/react-query").QueryObserverResult<import("@tanstack/react-query").InfiniteData<IRequestSuccess<TResponse>>, any>>;
138
+ remove: () => void;
139
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
140
+ };
@@ -0,0 +1,72 @@
1
+ import { useQueryClient, useInfiniteQuery } from '@tanstack/react-query';
2
+ import { useMemo, useEffect } from 'react';
3
+ import 'url-search-params-polyfill';
4
+ import { useEnvironmentVariables } from '../config/useEnvironmentVariables.js';
5
+ import { useQueryHeaders } from '../config/useQueryHeaders.js';
6
+ import 'axios';
7
+ import { makeRequest } from '../request/make-request.js';
8
+ import '../request/request.enum.js';
9
+
10
+ const useGetInfiniteRequest = ({ path, load = false, queryOptions, keyTracker, baseUrl, headers, }) => {
11
+ const { API_URL, TIMEOUT } = useEnvironmentVariables();
12
+ const { getHeaders } = useQueryHeaders();
13
+ let queryClient = useQueryClient();
14
+ // eslint-disable-next-line react-hooks/exhaustive-deps
15
+ queryClient = useMemo(() => queryClient, []);
16
+ const sendRequest = async (res, rej, pageParam) => {
17
+ if (load) {
18
+ // get request headers
19
+ const globalHeaders = getHeaders();
20
+ const getResponse = await makeRequest({
21
+ path: pageParam ?? path,
22
+ headers: { ...globalHeaders, ...headers },
23
+ baseURL: baseUrl ?? API_URL,
24
+ timeout: TIMEOUT,
25
+ });
26
+ if (getResponse.status) {
27
+ res(getResponse);
28
+ }
29
+ else {
30
+ rej(getResponse);
31
+ }
32
+ }
33
+ else {
34
+ res(null);
35
+ }
36
+ };
37
+ const query = useInfiniteQuery([path, {}], ({ pageParam = path }) => new Promise((res, rej) => sendRequest(res, rej, pageParam)), {
38
+ enabled: load,
39
+ getNextPageParam: (lastPage, pages) => constructPaginationLink('next_page', lastPage, pages),
40
+ getPreviousPageParam: (lastPage, pages) => constructPaginationLink('previous_page', lastPage, pages),
41
+ ...queryOptions,
42
+ });
43
+ /**
44
+ *
45
+ * This pagination implementation is currently tied to our use case
46
+ */
47
+ const constructPaginationLink = (direction, lastPage, pages) => {
48
+ const [pathname, queryString] = lastPage.split('?');
49
+ const queryParams = new URLSearchParams(queryString);
50
+ const lastPageItem = pages[pages.length - 1];
51
+ queryParams.set('page', String(lastPageItem?.data.pagination[direction]));
52
+ return pathname + '?' + queryParams.toString();
53
+ };
54
+ useEffect(() => {
55
+ if (keyTracker) {
56
+ // set expiration time for the tracker
57
+ queryClient.setQueryDefaults([keyTracker], {
58
+ cacheTime: Infinity,
59
+ staleTime: Infinity,
60
+ });
61
+ queryClient.setQueryData([keyTracker], [path, {}]);
62
+ }
63
+ }, [keyTracker, path, queryClient, queryOptions?.staleTime]);
64
+ const flattenedData = query.data?.pages.flat() ?? [];
65
+ return {
66
+ ...query,
67
+ flattenedData,
68
+ };
69
+ };
70
+
71
+ export { useGetInfiniteRequest };
72
+ //# sourceMappingURL=useGetInfiniteRequest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useGetInfiniteRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -16,12 +16,13 @@ const useGetRequest = ({ path, load = false, queryOptions, keyTracker, baseUrl,
16
16
  let queryClient = useQueryClient();
17
17
  // eslint-disable-next-line react-hooks/exhaustive-deps
18
18
  queryClient = useMemo(() => queryClient, []);
19
- const sendRequest = async (res, rej) => {
19
+ const sendRequest = async (res, rej, queryKey) => {
20
20
  if (load) {
21
21
  // get request headers
22
22
  const globalHeaders = getHeaders();
23
+ const [url] = (queryKey ?? []);
23
24
  const getResponse = await makeRequest({
24
- path: requestPath,
25
+ path: url ?? requestPath,
25
26
  headers: { ...globalHeaders, ...headers },
26
27
  baseURL: baseUrl ?? API_URL,
27
28
  timeout: TIMEOUT,
@@ -37,7 +38,7 @@ const useGetRequest = ({ path, load = false, queryOptions, keyTracker, baseUrl,
37
38
  res(null);
38
39
  }
39
40
  };
40
- const query = useQuery([requestPath, {}], () => new Promise((res, rej) => sendRequest(res, rej)), {
41
+ const query = useQuery([requestPath, {}], ({ queryKey }) => new Promise((res, rej) => sendRequest(res, rej, queryKey)), {
41
42
  enabled: load,
42
43
  ...options,
43
44
  });
@@ -1 +1 @@
1
- {"version":3,"file":"useGetRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"useGetRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ventlio/tanstack-query",
3
- "version": "0.2.72",
3
+ "version": "0.2.74",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "contributors": [
@@ -1,5 +1,6 @@
1
1
  export * from './queries.interface';
2
2
  export * from './useDeleteRequest';
3
+ export * from './useGetInfiniteRequest';
3
4
  export * from './useGetRequest';
4
5
  export * from './usePatchRequest';
5
6
  export * from './usePostRequest';
@@ -1,4 +1,4 @@
1
- import type { UseQueryOptions } from '@tanstack/react-query';
1
+ import type { UseInfiniteQueryOptions, UseQueryOptions } from '@tanstack/react-query';
2
2
  import type { RawAxiosRequestHeaders } from 'axios';
3
3
  import type { IRequestError, IRequestSuccess } from '../request';
4
4
 
@@ -18,6 +18,13 @@ export type TanstackQueryOption<TResponse> = UseQueryOptions<
18
18
  Array<any>
19
19
  >;
20
20
 
21
+ export type TanstackInfiniteQueryOption<TResponse> = UseInfiniteQueryOptions<
22
+ IRequestSuccess<TResponse | undefined>,
23
+ IRequestError,
24
+ IRequestSuccess<TResponse | undefined>,
25
+ Array<any>
26
+ >;
27
+
21
28
  export interface DefaultRequestOptions {
22
29
  baseUrl?: string;
23
30
  headers?: RawAxiosRequestHeaders;
@@ -1,4 +1,4 @@
1
- import type { UseQueryOptions } from '@tanstack/react-query';
1
+ import type { QueryKey, UseQueryOptions } from '@tanstack/react-query';
2
2
  import { useQuery } from '@tanstack/react-query';
3
3
  import type { RawAxiosRequestHeaders } from 'axios';
4
4
  import { useState } from 'react';
@@ -16,12 +16,14 @@ export const useDeleteRequest = <TResponse>(deleteOptions?: DefaultRequestOption
16
16
 
17
17
  const { getHeaders } = useQueryHeaders();
18
18
 
19
- const sendRequest = async (res: (value: any) => void, rej: (reason?: any) => void) => {
19
+ const sendRequest = async (res: (value: any) => void, rej: (reason?: any) => void, queryKey?: QueryKey) => {
20
20
  // get request headers
21
21
  const globalHeaders: RawAxiosRequestHeaders = getHeaders();
22
22
 
23
+ const [url] = (queryKey ?? []) as string[];
24
+
23
25
  const postResponse = await makeRequest<TResponse>({
24
- path: requestPath,
26
+ path: url ?? requestPath,
25
27
  headers: { ...globalHeaders, ...headers },
26
28
  method: HttpMethod.DELETE,
27
29
  baseURL: baseUrl ?? API_URL,
@@ -37,7 +39,8 @@ export const useDeleteRequest = <TResponse>(deleteOptions?: DefaultRequestOption
37
39
 
38
40
  const query = useQuery<any, any, IRequestSuccess<TResponse>>(
39
41
  [requestPath, {}],
40
- () => new Promise<IRequestSuccess<TResponse> | IRequestError>((res, rej) => sendRequest(res, rej)),
42
+ ({ queryKey }) =>
43
+ new Promise<IRequestSuccess<TResponse> | IRequestError>((res, rej) => sendRequest(res, rej, queryKey)),
41
44
  { enabled: false, ...options }
42
45
  );
43
46
 
@@ -0,0 +1,121 @@
1
+ import { useInfiniteQuery, useQueryClient } from '@tanstack/react-query';
2
+ import type { RawAxiosRequestHeaders } from 'axios';
3
+ import { useEffect, useMemo } from 'react';
4
+ import { useEnvironmentVariables, useQueryHeaders } from '../config';
5
+
6
+ import type { IRequestError, IRequestSuccess } from '../request';
7
+ import { makeRequest } from '../request';
8
+ import type { DefaultRequestOptions, TanstackInfiniteQueryOption } from './queries.interface';
9
+
10
+ interface Pagination {
11
+ previous_page: number;
12
+ current_page: number;
13
+ next_page: number;
14
+ size: number;
15
+ page_count: number;
16
+ total: number;
17
+ }
18
+
19
+ export const useGetInfiniteRequest = <TResponse extends Record<string, any>>({
20
+ path,
21
+ load = false,
22
+ queryOptions,
23
+ keyTracker,
24
+ baseUrl,
25
+ headers,
26
+ }: {
27
+ path: string;
28
+ load?: boolean;
29
+ queryOptions?: TanstackInfiniteQueryOption<TResponse>;
30
+ keyTracker?: string;
31
+ } & DefaultRequestOptions) => {
32
+ const { API_URL, TIMEOUT } = useEnvironmentVariables();
33
+ const { getHeaders } = useQueryHeaders();
34
+
35
+ let queryClient = useQueryClient();
36
+
37
+ // eslint-disable-next-line react-hooks/exhaustive-deps
38
+ queryClient = useMemo(() => queryClient, []);
39
+
40
+ const sendRequest = async (
41
+ res: (
42
+ value: IRequestError | IRequestSuccess<TResponse> | PromiseLike<IRequestError | IRequestSuccess<TResponse>>
43
+ ) => void,
44
+ rej: (reason?: any) => void,
45
+ pageParam?: string
46
+ ) => {
47
+ if (load) {
48
+ // get request headers
49
+ const globalHeaders: RawAxiosRequestHeaders = getHeaders();
50
+
51
+ const getResponse = await makeRequest<TResponse>({
52
+ path: pageParam ?? path,
53
+ headers: { ...globalHeaders, ...headers },
54
+ baseURL: baseUrl ?? API_URL,
55
+ timeout: TIMEOUT,
56
+ });
57
+
58
+ if (getResponse.status) {
59
+ res(getResponse as IRequestSuccess<TResponse>);
60
+ } else {
61
+ rej(getResponse);
62
+ }
63
+ } else {
64
+ res(null as any);
65
+ }
66
+ };
67
+
68
+ const query = useInfiniteQuery<any, any, IRequestSuccess<TResponse>>(
69
+ [path, {}],
70
+ ({ pageParam = path }) =>
71
+ new Promise<IRequestSuccess<TResponse> | IRequestError>((res, rej) => sendRequest(res, rej, pageParam)),
72
+ {
73
+ enabled: load,
74
+ getNextPageParam: (lastPage, pages) => constructPaginationLink('next_page', lastPage, pages),
75
+ getPreviousPageParam: (lastPage, pages) => constructPaginationLink('previous_page', lastPage, pages),
76
+ ...(queryOptions as any),
77
+ }
78
+ );
79
+
80
+ /**
81
+ *
82
+ * This pagination implementation is currently tied to our use case
83
+ */
84
+ const constructPaginationLink = (
85
+ direction: 'next_page' | 'previous_page',
86
+ lastPage: string,
87
+ pages: IRequestSuccess<
88
+ TResponse & {
89
+ pagination: Pagination;
90
+ }
91
+ >[]
92
+ ) => {
93
+ const [pathname, queryString] = lastPage.split('?');
94
+
95
+ const queryParams = new URLSearchParams(queryString);
96
+ const lastPageItem = pages[pages.length - 1];
97
+
98
+ queryParams.set('page', String(lastPageItem?.data.pagination[direction]));
99
+
100
+ return pathname + '?' + queryParams.toString();
101
+ };
102
+
103
+ useEffect(() => {
104
+ if (keyTracker) {
105
+ // set expiration time for the tracker
106
+ queryClient.setQueryDefaults([keyTracker], {
107
+ cacheTime: Infinity,
108
+ staleTime: Infinity,
109
+ });
110
+
111
+ queryClient.setQueryData([keyTracker], [path, {}]);
112
+ }
113
+ }, [keyTracker, path, queryClient, queryOptions?.staleTime]);
114
+
115
+ const flattenedData = query.data?.pages.flat() ?? [];
116
+
117
+ return {
118
+ ...query,
119
+ flattenedData,
120
+ };
121
+ };
@@ -1,4 +1,4 @@
1
- import type { UseQueryOptions } from '@tanstack/react-query';
1
+ import type { QueryKey, UseQueryOptions } from '@tanstack/react-query';
2
2
  import { useQuery, useQueryClient } from '@tanstack/react-query';
3
3
  import { startTransition, useEffect, useMemo, useState } from 'react';
4
4
  import type { RawAxiosRequestHeaders } from '../../node_modules/axios/index';
@@ -37,14 +37,17 @@ export const useGetRequest = <TResponse extends Record<string, any>>({
37
37
  res: (
38
38
  value: IRequestError | IRequestSuccess<TResponse> | PromiseLike<IRequestError | IRequestSuccess<TResponse>>
39
39
  ) => void,
40
- rej: (reason?: any) => void
40
+ rej: (reason?: any) => void,
41
+ queryKey?: QueryKey
41
42
  ) => {
42
43
  if (load) {
43
44
  // get request headers
44
45
  const globalHeaders: RawAxiosRequestHeaders = getHeaders();
45
46
 
47
+ const [url] = (queryKey ?? []) as string[];
48
+
46
49
  const getResponse = await makeRequest<TResponse>({
47
- path: requestPath,
50
+ path: url ?? requestPath,
48
51
  headers: { ...globalHeaders, ...headers },
49
52
  baseURL: baseUrl ?? API_URL,
50
53
  timeout: TIMEOUT,
@@ -62,7 +65,8 @@ export const useGetRequest = <TResponse extends Record<string, any>>({
62
65
 
63
66
  const query = useQuery<any, any, IRequestSuccess<TResponse>>(
64
67
  [requestPath, {}],
65
- () => new Promise<IRequestSuccess<TResponse> | IRequestError>((res, rej) => sendRequest(res, rej)),
68
+ ({ queryKey }) =>
69
+ new Promise<IRequestSuccess<TResponse> | IRequestError>((res, rej) => sendRequest(res, rej, queryKey)),
66
70
  {
67
71
  enabled: load,
68
72
  ...options,