1
- import { useContext } from "react" ;
1
+ // import { useContext } from "react";
2
2
3
+ // import { getXRay } from "@refinedev/devtools-internal";
4
+ // import {
5
+ // type UseQueryOptions,
6
+ // type UseQueryResult,
7
+ // useQuery,
8
+ // } from "@tanstack/react-query";
9
+
10
+ // import { AccessControlContext } from "@contexts/accessControl";
11
+ // import { sanitizeResource } from "@definitions/helpers/sanitize-resource";
12
+ // import { useKeys } from "@hooks/useKeys";
13
+ // import type {
14
+ // CanParams,
15
+ // CanReturnType,
16
+ // } from "../../../contexts/accessControl/types";
17
+
18
+ // export type UseCanProps = CanParams & {
19
+ // /**
20
+ // * react-query's [useQuery](https://tanstack.com/query/v4/docs/reference/useQuery) options
21
+ // */
22
+ // queryOptions?: UseQueryOptions<CanReturnType>;
23
+ // };
24
+
25
+ // /**
26
+ // * `useCan` uses the `can` as the query function for `react-query`'s {@link https://react-query.tanstack.com/guides/queries `useQuery`}. It takes the parameters that `can` takes. It can also be configured with `queryOptions` for `useQuery`. Returns the result of `useQuery`.
27
+ // * @see {@link https://refine.dev/docs/api-reference/core/hooks/accessControl/useCan } for more details.
28
+ // *
29
+ // * @typeParam CanParams {@link https://refine.dev/docs/core/interfaceReferences#canparams}
30
+ // * @typeParam CanReturnType {@link https://refine.dev/docs/core/interfaceReferences#canreturntype}
31
+ // *
32
+ // */
33
+ // export const useCan = ({
34
+ // action,
35
+ // resource,
36
+ // params,
37
+ // queryOptions: hookQueryOptions,
38
+ // }: UseCanProps): UseQueryResult<CanReturnType> => {
39
+ // const { can, options: globalOptions } = useContext(AccessControlContext);
40
+ // const { keys, preferLegacyKeys } = useKeys();
41
+
42
+ // const { queryOptions: globalQueryOptions } = globalOptions || {};
43
+
44
+ // const mergedQueryOptions = {
45
+ // ...globalQueryOptions,
46
+ // ...hookQueryOptions,
47
+ // };
48
+
49
+ // /**
50
+ // * Since `react-query` stringifies the query keys, it will throw an error for a circular dependency if we include `React.ReactNode` elements inside the keys.
51
+ // * The feature in #2220(https://github.com/refinedev/refine/issues/2220) includes such change and to fix this, we need to remove `icon` property in the `resource`
52
+ // */
53
+ // const { resource: _resource, ...paramsRest } = params ?? {};
54
+
55
+ // const sanitizedResource = sanitizeResource(_resource);
56
+
57
+ // const queryResponse = useQuery<CanReturnType>({
58
+ // queryKey: keys()
59
+ // .access()
60
+ // .resource(resource)
61
+ // .action(action)
62
+ // .params({
63
+ // params: { ...paramsRest, resource: sanitizedResource },
64
+ // enabled: mergedQueryOptions?.enabled,
65
+ // })
66
+ // .get(preferLegacyKeys),
67
+ // // Enabled check for `can` is enough to be sure that it's defined in the query function but TS is not smart enough to know that.
68
+ // queryFn: () =>
69
+ // can?.({
70
+ // action,
71
+ // resource,
72
+ // params: { ...paramsRest, resource: sanitizedResource },
73
+ // }) ?? Promise.resolve({ can: true }),
74
+ // enabled: typeof can !== "undefined",
75
+ // ...mergedQueryOptions,
76
+ // meta: {
77
+ // ...mergedQueryOptions?.meta,
78
+ // ...getXRay("useCan", preferLegacyKeys, resource, [
79
+ // "useButtonCanAccess",
80
+ // "useNavigationButton",
81
+ // ]),
82
+ // },
83
+ // retry: false,
84
+ // });
85
+
86
+ // return typeof can === "undefined"
87
+ // ? ({ data: { can: true } } as typeof queryResponse)
88
+ // : queryResponse;
89
+ // };
90
+
91
+ import { useContext , useRef } from "react" ;
3
92
import { getXRay } from "@refinedev/devtools-internal" ;
4
93
import {
5
94
type UseQueryOptions ,
6
95
type UseQueryResult ,
7
96
useQuery ,
8
97
} from "@tanstack/react-query" ;
9
-
10
98
import { AccessControlContext } from "@contexts/accessControl" ;
11
99
import { sanitizeResource } from "@definitions/helpers/sanitize-resource" ;
12
100
import { useKeys } from "@hooks/useKeys" ;
@@ -23,8 +111,13 @@ export type UseCanProps = CanParams & {
23
111
} ;
24
112
25
113
/**
26
- * `useCan` uses the `can` as the query function for `react-query`'s {@link https://react-query.tanstack.com/guides/queries `useQuery`}. It takes the parameters that `can` takes. It can also be configured with `queryOptions` for `useQuery`. Returns the result of `useQuery`.
27
- * @see {@link https://refine.dev/docs/api-reference/core/hooks/accessControl/useCan } for more details.
114
+ * Custom cache for `can` function results to optimize performance.
115
+ */
116
+ const canCache = new Map < string , { result : CanReturnType ; timestamp : number } > ( ) ;
117
+
118
+ /**
119
+ * `useCan` uses the `can` as the query function for `react-query`'s {@link https://react-query.tanstack.com/guides/queries `useQuery`}.
120
+ * It now includes a custom cache for `can` results to optimize performance by reducing repeated calls.
28
121
*
29
122
* @typeParam CanParams {@link https://refine.dev/docs/core/interfaceReferences#canparams}
30
123
* @typeParam CanReturnType {@link https://refine.dev/docs/core/interfaceReferences#canreturntype}
@@ -39,22 +132,39 @@ export const useCan = ({
39
132
const { can, options : globalOptions } = useContext ( AccessControlContext ) ;
40
133
const { keys, preferLegacyKeys } = useKeys ( ) ;
41
134
42
- const { queryOptions : globalQueryOptions } = globalOptions || { } ;
43
-
44
- const mergedQueryOptions = {
45
- ...globalQueryOptions ,
46
- ...hookQueryOptions ,
135
+ const mergedQueryOptions : UseQueryOptions < CanReturnType > = {
136
+ ...( globalOptions ?. queryOptions ?? { } ) ,
137
+ ...( hookQueryOptions ?? { } ) ,
138
+ enabled :
139
+ hookQueryOptions ?. enabled ??
140
+ globalOptions ?. queryOptions ?. enabled ??
141
+ typeof can !== "undefined" ,
142
+ meta : {
143
+ ...( globalOptions ?. queryOptions ?. meta ?? { } ) ,
144
+ ...( hookQueryOptions ?. meta ?? { } ) ,
145
+ ...getXRay ( "useCan" , preferLegacyKeys , resource , [
146
+ "useButtonCanAccess" ,
147
+ "useNavigationButton" ,
148
+ ] ) ,
149
+ } ,
150
+ retry : false ,
47
151
} ;
48
152
49
- /**
50
- * Since `react-query` stringifies the query keys, it will throw an error for a circular dependency if we include `React.ReactNode` elements inside the keys.
51
- * The feature in #2220(https://github.com/refinedev/refine/issues/2220) includes such change and to fix this, we need to remove `icon` property in the `resource`
52
- */
53
153
const { resource : _resource , ...paramsRest } = params ?? { } ;
54
-
55
154
const sanitizedResource = sanitizeResource ( _resource ) ;
56
155
156
+ // Custom caching logic
157
+ const cacheKey = `${ resource } -${ action } -${ JSON . stringify ( paramsRest ) } ` ;
158
+ const cacheDuration = mergedQueryOptions ?. staleTime || 5 * 60 * 1000 ; // Default to 5 minutes
159
+ const cacheEntry = canCache . get ( cacheKey ) ;
160
+
161
+ // Check if we have a cached value and if it's still valid
162
+ if ( cacheEntry && Date . now ( ) - cacheEntry . timestamp < cacheDuration ) {
163
+ return { data : cacheEntry . result } as UseQueryResult < CanReturnType > ;
164
+ }
165
+
57
166
const queryResponse = useQuery < CanReturnType > ( {
167
+ ...mergedQueryOptions ,
58
168
queryKey : keys ( )
59
169
. access ( )
60
170
. resource ( resource )
@@ -64,26 +174,22 @@ export const useCan = ({
64
174
enabled : mergedQueryOptions ?. enabled ,
65
175
} )
66
176
. get ( preferLegacyKeys ) ,
67
- // Enabled check for `can` is enough to be sure that it's defined in the query function but TS is not smart enough to know that.
68
- queryFn : ( ) =>
69
- can ?.( {
177
+ queryFn : async ( ) => {
178
+ const result = await ( can ?.( {
70
179
action,
71
180
resource,
72
181
params : { ...paramsRest , resource : sanitizedResource } ,
73
- } ) ?? Promise . resolve ( { can : true } ) ,
74
- enabled : typeof can !== "undefined" ,
75
- ...mergedQueryOptions ,
76
- meta : {
77
- ...mergedQueryOptions ?. meta ,
78
- ...getXRay ( "useCan" , preferLegacyKeys , resource , [
79
- "useButtonCanAccess" ,
80
- "useNavigationButton" ,
81
- ] ) ,
182
+ } ) ?? Promise . resolve ( { can : true } ) ) ;
183
+
184
+ // Cache the result for future use
185
+ canCache . set ( cacheKey , { result, timestamp : Date . now ( ) } ) ;
186
+
187
+ return result ;
82
188
} ,
189
+ enabled : typeof can !== "undefined" ,
190
+ meta : mergedQueryOptions . meta ,
83
191
retry : false ,
84
192
} ) ;
85
193
86
- return typeof can === "undefined"
87
- ? ( { data : { can : true } } as typeof queryResponse )
88
- : queryResponse ;
194
+ return queryResponse ;
89
195
} ;
0 commit comments