@@ -7,87 +7,158 @@ import React, {
7
7
useContext ,
8
8
useMemo ,
9
9
useRef ,
10
+ useState ,
10
11
} from 'react'
12
+ import { KEY_IS_NOT_STRING_ERROR , StatusEnum } from './constants'
13
+ import DataLoader from './dataloader'
14
+ import { OnErrorFn , PromiseType } from './types'
15
+
16
+ type RequestQueue = Record < string , DataLoader >
17
+ type CachedData = Record < string , unknown >
18
+ type Reloads = Record < string , ( ) => Promise < void | unknown > >
19
+
20
+ type UseDataLoaderInitializerArgs < T = unknown > = {
21
+ enabled ?: boolean
22
+ key : string
23
+ status ?: StatusEnum
24
+ method : ( ) => PromiseType < T >
25
+ pollingInterval ?: number
26
+ keepPreviousData ?: boolean
27
+ }
11
28
12
29
interface Context {
13
- addCachedData : ( key : string , newData : unknown ) => void ;
14
- addReload : ( key : string , method : ( ) => Promise < void > ) => void ;
15
- cacheKeyPrefix : string ;
30
+ addCachedData : ( key : string , newData : unknown ) => void
31
+ addReload : ( key : string , method : ( ) => Promise < void | unknown > ) => void
32
+ addRequest : ( key : string , args : UseDataLoaderInitializerArgs ) => DataLoader
33
+ cacheKeyPrefix : string
16
34
onError ?: ( error : Error ) => void | Promise < void >
17
- clearAllCachedData : ( ) => void ;
18
- clearAllReloads : ( ) => void ;
19
- clearCachedData : ( key ?: string | undefined ) => void ;
20
- clearReload : ( key ?: string | undefined ) => void ;
21
- getCachedData : ( key ?: string | undefined ) => unknown ;
22
- getReloads : ( key ?: string | undefined ) => ( ( ) => Promise < void > ) | Reloads ;
23
- reload : ( key ?: string | undefined ) => Promise < void > ;
24
- reloadAll : ( ) => Promise < void > ;
35
+ clearAllCachedData : ( ) => void
36
+ clearAllReloads : ( ) => void
37
+ clearCachedData : ( key ?: string ) => void
38
+ clearReload : ( key ?: string ) => void
39
+ getCachedData : ( key ?: string ) => unknown | CachedData
40
+ getReloads : ( key ?: string ) => ( ( ) => Promise < void | unknown > ) | Reloads
41
+ getRequest : ( key : string ) => DataLoader | undefined
42
+ reload : ( key ?: string ) => Promise < void >
43
+ reloadAll : ( ) => Promise < void >
25
44
}
26
45
27
- type CachedData = Record < string , unknown >
28
- type Reloads = Record < string , ( ) => Promise < void > >
29
-
30
46
// @ts -expect-error we force the context to undefined, should be corrected with default values
31
47
export const DataLoaderContext = createContext < Context > ( undefined )
32
48
33
- const DataLoaderProvider = ( { children, cacheKeyPrefix, onError } : {
34
- children : ReactNode , cacheKeyPrefix : string , onError : ( error : Error ) => void | Promise < void >
49
+ const DataLoaderProvider = ( {
50
+ children,
51
+ cacheKeyPrefix,
52
+ onError,
53
+ } : {
54
+ children : ReactNode
55
+ cacheKeyPrefix : string
56
+ onError : OnErrorFn
35
57
} ) : ReactElement => {
36
- const cachedData = useRef < CachedData > ( { } )
58
+ const [ requestQueue , setRequestQueue ] = useState ( { } as RequestQueue )
59
+ const [ cachedData , setCachedDataPrivate ] = useState < CachedData > ( { } )
37
60
const reloads = useRef < Reloads > ( { } )
38
61
39
- const setCachedData = useCallback ( ( compute : CachedData | ( ( data : CachedData ) => CachedData ) ) => {
40
- if ( typeof compute === 'function' ) {
41
- cachedData . current = compute ( cachedData . current )
42
- } else {
43
- cachedData . current = compute
44
- }
45
- } , [ ] )
62
+ const computeKey = useCallback (
63
+ ( key : string ) => `${ cacheKeyPrefix ? `${ cacheKeyPrefix } -` : '' } ${ key } ` ,
64
+ [ cacheKeyPrefix ] ,
65
+ )
46
66
47
- const setReloads = useCallback ( ( compute : Reloads | ( ( data : Reloads ) => Reloads ) ) => {
48
- if ( typeof compute === 'function' ) {
49
- reloads . current = compute ( reloads . current )
50
- } else {
51
- reloads . current = compute
52
- }
53
- } , [ ] )
67
+ const setCachedData = useCallback (
68
+ ( compute : CachedData | ( ( data : CachedData ) => CachedData ) ) => {
69
+ if ( typeof compute === 'function' ) {
70
+ setCachedDataPrivate ( current => compute ( current ) )
71
+ } else {
72
+ setCachedDataPrivate ( compute )
73
+ }
74
+ } ,
75
+ [ ] ,
76
+ )
77
+
78
+ const setReloads = useCallback (
79
+ ( compute : Reloads | ( ( data : Reloads ) => Reloads ) ) => {
80
+ if ( typeof compute === 'function' ) {
81
+ reloads . current = compute ( reloads . current )
82
+ } else {
83
+ reloads . current = compute
84
+ }
85
+ } ,
86
+ [ ] ,
87
+ )
54
88
55
89
const addCachedData = useCallback (
56
90
( key : string , newData : unknown ) => {
57
- if ( key && typeof key === 'string' && newData ) {
58
- setCachedData ( actualCachedData => ( {
59
- ...actualCachedData ,
60
- [ key ] : newData ,
61
- } ) )
91
+ if ( newData ) {
92
+ if ( key && typeof key === 'string' ) {
93
+ setCachedData ( actualCachedData => ( {
94
+ ...actualCachedData ,
95
+ [ computeKey ( key ) ] : newData ,
96
+ } ) )
97
+ } else throw new Error ( KEY_IS_NOT_STRING_ERROR )
62
98
}
63
99
} ,
64
- [ setCachedData ] ,
100
+ [ setCachedData , computeKey ] ,
65
101
)
66
102
67
103
const addReload = useCallback (
68
- ( key : string , method : ( ) => Promise < void > ) => {
69
- if ( key && typeof key === 'string' && method ) {
70
- setReloads ( actualReloads => ( {
71
- ...actualReloads ,
72
- [ key ] : method ,
104
+ ( key : string , method : ( ) => Promise < void | unknown > ) => {
105
+ if ( method ) {
106
+ if ( key && typeof key === 'string' ) {
107
+ setReloads ( actualReloads => ( {
108
+ ...actualReloads ,
109
+ [ computeKey ( key ) ] : method ,
110
+ } ) )
111
+ } else throw new Error ( KEY_IS_NOT_STRING_ERROR )
112
+ }
113
+ } ,
114
+ [ setReloads , computeKey ] ,
115
+ )
116
+
117
+ const addRequest = useCallback (
118
+ ( key : string , args : UseDataLoaderInitializerArgs ) => {
119
+ if ( key && typeof key === 'string' ) {
120
+ const notifyChanges = ( updatedRequest : DataLoader ) => {
121
+ setRequestQueue ( current => ( {
122
+ ...current ,
123
+ [ computeKey ( updatedRequest . key ) ] : updatedRequest ,
124
+ } ) )
125
+ }
126
+ const newRequest = new DataLoader ( { ...args , notify : notifyChanges } )
127
+ newRequest . addOnSuccessListener ( result => {
128
+ if ( result !== undefined && result !== null )
129
+ addCachedData ( key , result )
130
+ } )
131
+ setRequestQueue ( current => ( {
132
+ ...current ,
133
+ [ computeKey ( key ) ] : newRequest ,
73
134
} ) )
135
+
136
+ addReload ( key , newRequest . launch )
137
+
138
+ return newRequest
74
139
}
140
+ throw new Error ( KEY_IS_NOT_STRING_ERROR )
75
141
} ,
76
- [ setReloads ] ,
142
+ [ computeKey , addCachedData , addReload ] ,
143
+ )
144
+
145
+ const getRequest = useCallback (
146
+ ( key : string ) => requestQueue [ computeKey ( key ) ] ,
147
+ [ computeKey , requestQueue ] ,
77
148
)
78
149
79
150
const clearReload = useCallback (
80
151
( key ?: string ) => {
81
152
if ( key && typeof key === 'string' ) {
82
153
setReloads ( actualReloads => {
83
154
const tmp = actualReloads
84
- delete tmp [ key ]
155
+ delete tmp [ computeKey ( key ) ]
85
156
86
157
return tmp
87
158
} )
88
- }
159
+ } else throw new Error ( KEY_IS_NOT_STRING_ERROR )
89
160
} ,
90
- [ setReloads ] ,
161
+ [ setReloads , computeKey ] ,
91
162
)
92
163
93
164
const clearAllReloads = useCallback ( ( ) => {
@@ -99,70 +170,83 @@ const DataLoaderProvider = ({ children, cacheKeyPrefix, onError }: {
99
170
if ( key && typeof key === 'string' ) {
100
171
setCachedData ( actualCachedData => {
101
172
const tmp = actualCachedData
102
- delete tmp [ key ]
173
+ delete tmp [ computeKey ( key ) ]
103
174
104
175
return tmp
105
176
} )
106
- }
177
+ } else throw new Error ( KEY_IS_NOT_STRING_ERROR )
107
178
} ,
108
- [ setCachedData ] ,
179
+ [ setCachedData , computeKey ] ,
109
180
)
110
181
const clearAllCachedData = useCallback ( ( ) => {
111
182
setCachedData ( { } )
112
183
} , [ setCachedData ] )
113
184
114
- const reload = useCallback ( async ( key ?: string ) => {
115
- if ( key && typeof key === 'string' ) {
116
- await ( reloads . current [ key ] && reloads . current [ key ] ( ) )
117
- }
118
- } , [ ] )
185
+ const reload = useCallback (
186
+ async ( key ?: string ) => {
187
+ if ( key && typeof key === 'string' ) {
188
+ await reloads . current [ computeKey ( key ) ] ?.( )
189
+ } else throw new Error ( KEY_IS_NOT_STRING_ERROR )
190
+ } ,
191
+ [ computeKey ] ,
192
+ )
119
193
120
194
const reloadAll = useCallback ( async ( ) => {
121
195
await Promise . all (
122
196
Object . values ( reloads . current ) . map ( reloadFn => reloadFn ( ) ) ,
123
197
)
124
198
} , [ ] )
125
199
126
- const getCachedData = useCallback ( ( key ?: string ) => {
127
- if ( key ) {
128
- return cachedData . current [ key ] || undefined
129
- }
200
+ const getCachedData = useCallback (
201
+ ( key ?: string ) => {
202
+ if ( key ) {
203
+ return cachedData [ computeKey ( key ) ]
204
+ }
130
205
131
- return cachedData . current
132
- } , [ ] )
206
+ return cachedData
207
+ } ,
208
+ [ computeKey , cachedData ] ,
209
+ )
133
210
134
- const getReloads = useCallback ( ( key ?: string ) => {
135
- if ( key ) {
136
- return reloads . current [ key ] || undefined
137
- }
211
+ const getReloads = useCallback (
212
+ ( key ?: string ) => {
213
+ if ( key ) {
214
+ return reloads . current [ computeKey ( key ) ]
215
+ }
138
216
139
- return reloads . current
140
- } , [ ] )
217
+ return reloads . current
218
+ } ,
219
+ [ computeKey ] ,
220
+ )
141
221
142
222
const value = useMemo (
143
223
( ) => ( {
144
224
addCachedData,
145
225
addReload,
226
+ addRequest,
146
227
cacheKeyPrefix,
147
228
clearAllCachedData,
148
229
clearAllReloads,
149
230
clearCachedData,
150
231
clearReload,
151
232
getCachedData,
152
233
getReloads,
234
+ getRequest,
153
235
onError,
154
236
reload,
155
237
reloadAll,
156
238
} ) ,
157
239
[
158
240
addCachedData ,
159
241
addReload ,
242
+ addRequest ,
160
243
cacheKeyPrefix ,
161
244
clearAllCachedData ,
162
245
clearAllReloads ,
163
246
clearCachedData ,
164
247
clearReload ,
165
248
getCachedData ,
249
+ getRequest ,
166
250
getReloads ,
167
251
onError ,
168
252
reload ,
0 commit comments