@@ -5,21 +5,23 @@ import React, {
55 createContext ,
66 useCallback ,
77 useContext ,
8+ useEffect ,
89 useMemo ,
910 useRef ,
10- useState ,
1111} from 'react'
12- import { KEY_IS_NOT_STRING_ERROR , StatusEnum } from './constants'
12+ import {
13+ DEFAULT_MAX_CONCURRENT_REQUESTS ,
14+ KEY_IS_NOT_STRING_ERROR ,
15+ StatusEnum ,
16+ } from './constants'
1317import DataLoader from './dataloader'
1418import { OnErrorFn , PromiseType } from './types'
1519
16- type RequestQueue = Record < string , DataLoader >
1720type CachedData = Record < string , unknown >
1821type Reloads = Record < string , ( ) => Promise < void | unknown > >
1922
2023type UseDataLoaderInitializerArgs < T = unknown > = {
2124 enabled ?: boolean
22- key : string
2325 status ?: StatusEnum
2426 method : ( ) => PromiseType < T >
2527 pollingInterval ?: number
@@ -30,226 +32,192 @@ type UseDataLoaderInitializerArgs<T = unknown> = {
3032 maxDataLifetime ?: number
3133}
3234
33- interface Context {
34- addCachedData : ( key : string , newData : unknown ) => void
35- addReload : ( key : string , method : ( ) => Promise < void | unknown > ) => void
35+ type GetCachedDataFn = {
36+ ( ) : CachedData
37+ ( key ?: string ) : unknown | undefined
38+ }
39+
40+ type GetReloadsFn = {
41+ ( ) : Reloads
42+ ( key ?: string ) : ( ( ) => Promise < void | unknown > ) | undefined
43+ }
44+
45+ export interface IDataLoaderContext {
3646 addRequest : ( key : string , args : UseDataLoaderInitializerArgs ) => DataLoader
37- cacheKeyPrefix : string
47+ getOrAddRequest : (
48+ key : string ,
49+ args : UseDataLoaderInitializerArgs ,
50+ ) => DataLoader
51+ cacheKeyPrefix ?: string
3852 onError ?: ( error : Error ) => void | Promise < void >
3953 clearAllCachedData : ( ) => void
40- clearAllReloads : ( ) => void
41- clearCachedData : ( key ?: string ) => void
42- clearReload : ( key ?: string ) => void
43- getCachedData : ( key ?: string ) => unknown | CachedData
44- getReloads : ( key ?: string ) => ( ( ) => Promise < void | unknown > ) | Reloads
45- getRequest : ( key : string ) => DataLoader | undefined
54+ clearCachedData : ( key : string ) => void
55+ getCachedData : GetCachedDataFn
56+ getReloads : GetReloadsFn
57+ getRequest : ( key : string ) => DataLoader
4658 reload : ( key ?: string ) => Promise < void >
4759 reloadAll : ( ) => Promise < void >
4860}
4961
5062// @ts -expect-error we force the context to undefined, should be corrected with default values
51- export const DataLoaderContext = createContext < Context > ( undefined )
63+ export const DataLoaderContext = createContext < IDataLoaderContext > ( undefined )
5264
5365const DataLoaderProvider = ( {
5466 children,
5567 cacheKeyPrefix,
5668 onError,
69+ maxConcurrentRequests,
5770} : {
5871 children : ReactNode
5972 cacheKeyPrefix : string
6073 onError : OnErrorFn
74+ maxConcurrentRequests ?: number
6175} ) : ReactElement => {
62- const [ requestQueue , setRequestQueue ] = useState ( { } as RequestQueue )
63- const [ cachedData , setCachedDataPrivate ] = useState < CachedData > ( { } )
64- const reloads = useRef < Reloads > ( { } )
65-
76+ const requestsRef = useRef < Record < string , DataLoader > > ( { } )
6677 const computeKey = useCallback (
6778 ( key : string ) => `${ cacheKeyPrefix ? `${ cacheKeyPrefix } -` : '' } ${ key } ` ,
6879 [ cacheKeyPrefix ] ,
6980 )
7081
71- const setCachedData = useCallback (
72- ( compute : CachedData | ( ( data : CachedData ) => CachedData ) ) => {
73- if ( typeof compute === 'function' ) {
74- setCachedDataPrivate ( current => compute ( current ) )
75- } else {
76- setCachedDataPrivate ( compute )
77- }
78- } ,
79- [ ] ,
80- )
81-
82- const setReloads = useCallback (
83- ( compute : Reloads | ( ( data : Reloads ) => Reloads ) ) => {
84- if ( typeof compute === 'function' ) {
85- reloads . current = compute ( reloads . current )
86- } else {
87- reloads . current = compute
88- }
89- } ,
90- [ ] ,
91- )
92-
93- const addCachedData = useCallback (
94- ( key : string , newData : unknown ) => {
95- if ( newData ) {
96- if ( key && typeof key === 'string' ) {
97- setCachedData ( actualCachedData => ( {
98- ...actualCachedData ,
99- [ computeKey ( key ) ] : newData ,
100- } ) )
101- } else throw new Error ( KEY_IS_NOT_STRING_ERROR )
102- }
103- } ,
104- [ setCachedData , computeKey ] ,
105- )
106-
107- const addReload = useCallback (
108- ( key : string , method : ( ) => Promise < void | unknown > ) => {
109- if ( method ) {
110- if ( key && typeof key === 'string' ) {
111- setReloads ( actualReloads => ( {
112- ...actualReloads ,
113- [ computeKey ( key ) ] : method ,
114- } ) )
115- } else throw new Error ( KEY_IS_NOT_STRING_ERROR )
116- }
117- } ,
118- [ setReloads , computeKey ] ,
82+ const getRequest = useCallback (
83+ ( key : string ) => requestsRef . current [ computeKey ( key ) ] ,
84+ [ computeKey ] ,
11985 )
12086
12187 const addRequest = useCallback (
12288 ( key : string , args : UseDataLoaderInitializerArgs ) => {
89+ if ( DataLoader . maxConcurrent !== maxConcurrentRequests ) {
90+ DataLoader . maxConcurrent = maxConcurrentRequests as number
91+ }
12392 if ( key && typeof key === 'string' ) {
124- const notifyChanges = ( updatedRequest : DataLoader ) => {
125- setRequestQueue ( current => ( {
126- ...current ,
127- [ computeKey ( updatedRequest . key ) ] : updatedRequest ,
128- } ) )
129- }
130- const newRequest = new DataLoader ( { ...args , notify : notifyChanges } )
131- newRequest . addOnSuccessListener ( result => {
132- if ( result !== undefined && result !== null )
133- addCachedData ( key , result )
93+ const newRequest = new DataLoader ( {
94+ ...args ,
95+ key : computeKey ( key ) ,
13496 } )
135- setRequestQueue ( current => ( {
136- ...current ,
137- [ computeKey ( key ) ] : newRequest ,
138- } ) )
13997
140- addReload ( key , ( ) => newRequest . load ( true ) )
98+ requestsRef . current [ newRequest . key ] = newRequest
14199
142100 return newRequest
143101 }
144102 throw new Error ( KEY_IS_NOT_STRING_ERROR )
145103 } ,
146- [ computeKey , addCachedData , addReload ] ,
104+ [ computeKey , maxConcurrentRequests ] ,
147105 )
148106
149- const getRequest = useCallback (
150- ( key : string ) => requestQueue [ computeKey ( key ) ] ,
151- [ computeKey , requestQueue ] ,
152- )
153-
154- const clearReload = useCallback (
155- ( key ?: string ) => {
156- if ( key && typeof key === 'string' ) {
157- setReloads ( actualReloads => {
158- const tmp = actualReloads
159- delete tmp [ computeKey ( key ) ]
107+ const getOrAddRequest = useCallback (
108+ ( key : string , args : UseDataLoaderInitializerArgs ) => {
109+ const requestFound = getRequest ( key )
110+ if ( ! requestFound ) {
111+ return addRequest ( key , args )
112+ }
160113
161- return tmp
162- } )
163- } else throw new Error ( KEY_IS_NOT_STRING_ERROR )
114+ return requestFound
164115 } ,
165- [ setReloads , computeKey ] ,
116+ [ addRequest , getRequest ] ,
166117 )
167118
168- const clearAllReloads = useCallback ( ( ) => {
169- setReloads ( { } )
170- } , [ setReloads ] )
171-
172119 const clearCachedData = useCallback (
173- ( key ?: string ) => {
174- if ( key && typeof key === 'string' ) {
175- setCachedData ( actualCachedData => {
176- const tmp = actualCachedData
177- delete tmp [ computeKey ( key ) ]
178-
179- return tmp
180- } )
120+ ( key : string ) => {
121+ if ( typeof key === 'string' ) {
122+ if ( requestsRef . current [ computeKey ( key ) ] ) {
123+ requestsRef . current [ computeKey ( key ) ] . clearData ( )
124+ }
181125 } else throw new Error ( KEY_IS_NOT_STRING_ERROR )
182126 } ,
183- [ setCachedData , computeKey ] ,
127+ [ computeKey ] ,
184128 )
185129 const clearAllCachedData = useCallback ( ( ) => {
186- setCachedData ( { } )
187- } , [ setCachedData ] )
130+ Object . values ( requestsRef . current ) . forEach ( request => {
131+ request . clearData ( )
132+ } )
133+ } , [ ] )
188134
189135 const reload = useCallback (
190136 async ( key ?: string ) => {
191137 if ( key && typeof key === 'string' ) {
192- await reloads . current [ computeKey ( key ) ] ?. ( )
138+ await getRequest ( key ) ?. load ( true )
193139 } else throw new Error ( KEY_IS_NOT_STRING_ERROR )
194140 } ,
195- [ computeKey ] ,
141+ [ getRequest ] ,
196142 )
197143
198144 const reloadAll = useCallback ( async ( ) => {
199145 await Promise . all (
200- Object . values ( reloads . current ) . map ( reloadFn => reloadFn ( ) ) ,
146+ Object . values ( requestsRef . current ) . map ( request => request . load ( true ) ) ,
201147 )
202148 } , [ ] )
203149
204150 const getCachedData = useCallback (
205151 ( key ?: string ) => {
206152 if ( key ) {
207- return cachedData [ computeKey ( key ) ]
153+ return getRequest ( key ) ?. getData ( )
208154 }
209155
210- return cachedData
156+ return Object . values ( requestsRef . current ) . reduce (
157+ ( acc , request ) => ( {
158+ ...acc ,
159+ [ request . key ] : request . getData ( ) ,
160+ } ) ,
161+ { } as CachedData ,
162+ )
211163 } ,
212- [ computeKey , cachedData ] ,
164+ [ getRequest ] ,
213165 )
214166
215167 const getReloads = useCallback (
216168 ( key ?: string ) => {
217169 if ( key ) {
218- return reloads . current [ computeKey ( key ) ]
170+ return getRequest ( key ) ? ( ) => getRequest ( key ) . load ( true ) : undefined
219171 }
220172
221- return reloads . current
173+ return Object . entries ( requestsRef . current ) . reduce (
174+ ( acc , [ requestKey , { load } ] ) => ( {
175+ ...acc ,
176+ [ requestKey ] : ( ) => load ( true ) ,
177+ } ) ,
178+ { } as Reloads ,
179+ )
222180 } ,
223- [ computeKey ] ,
181+ [ getRequest ] ,
224182 )
225183
184+ useEffect ( ( ) => {
185+ const cleanRequest = ( ) => {
186+ setTimeout ( ( ) => {
187+ Object . keys ( requestsRef . current ) . forEach ( key => {
188+ if ( requestsRef . current [ key ] . getObserversCount ( ) === 0 ) {
189+ requestsRef . current [ key ] . destroy ( )
190+ delete requestsRef . current [ key ]
191+ }
192+ } )
193+ cleanRequest ( )
194+ } , 300 )
195+ }
196+
197+ cleanRequest ( )
198+ } , [ ] )
199+
226200 const value = useMemo (
227201 ( ) => ( {
228- addCachedData,
229- addReload,
230202 addRequest,
231203 cacheKeyPrefix,
232204 clearAllCachedData,
233- clearAllReloads,
234205 clearCachedData,
235- clearReload,
236206 getCachedData,
207+ getOrAddRequest,
237208 getReloads,
238209 getRequest,
239210 onError,
240211 reload,
241212 reloadAll,
242213 } ) ,
243214 [
244- addCachedData ,
245- addReload ,
246215 addRequest ,
247216 cacheKeyPrefix ,
248217 clearAllCachedData ,
249- clearAllReloads ,
250218 clearCachedData ,
251- clearReload ,
252219 getCachedData ,
220+ getOrAddRequest ,
253221 getRequest ,
254222 getReloads ,
255223 onError ,
@@ -259,7 +227,7 @@ const DataLoaderProvider = ({
259227 )
260228
261229 return (
262- < DataLoaderContext . Provider value = { value } >
230+ < DataLoaderContext . Provider value = { value as IDataLoaderContext } >
263231 { children }
264232 </ DataLoaderContext . Provider >
265233 )
@@ -268,14 +236,17 @@ const DataLoaderProvider = ({
268236DataLoaderProvider . propTypes = {
269237 cacheKeyPrefix : PropTypes . string ,
270238 children : PropTypes . node . isRequired ,
239+ maxConcurrentRequests : PropTypes . number ,
271240 onError : PropTypes . func ,
272241}
273242
274243DataLoaderProvider . defaultProps = {
275244 cacheKeyPrefix : undefined ,
245+ maxConcurrentRequests : DEFAULT_MAX_CONCURRENT_REQUESTS ,
276246 onError : undefined ,
277247}
278248
279- export const useDataLoaderContext = ( ) : Context => useContext ( DataLoaderContext )
249+ export const useDataLoaderContext = ( ) : IDataLoaderContext =>
250+ useContext ( DataLoaderContext )
280251
281252export default DataLoaderProvider
0 commit comments