Skip to content

Commit fc28e6d

Browse files
feat: add needPolling callback (#520)
1 parent 88ccb26 commit fc28e6d

File tree

6 files changed

+113
-11
lines changed

6 files changed

+113
-11
lines changed

packages/use-dataloader/src/DataLoaderProvider.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
StatusEnum,
1616
} from './constants'
1717
import DataLoader from './dataloader'
18-
import { OnErrorFn, PromiseType } from './types'
18+
import { NeedPollingType, OnErrorFn, PromiseType } from './types'
1919

2020
type CachedData = Record<string, unknown>
2121
type Reloads = Record<string, () => Promise<void | unknown>>
@@ -30,6 +30,7 @@ type UseDataLoaderInitializerArgs<T = unknown> = {
3030
* Max time before data from previous success is considered as outdated (in millisecond)
3131
*/
3232
maxDataLifetime?: number
33+
needPolling?: NeedPollingType<T>
3334
}
3435

3536
type GetCachedDataFn = {

packages/use-dataloader/src/__tests__/dataloader.test.ts

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -193,23 +193,84 @@ describe('Dataloader class', () => {
193193
})
194194
await instance.load()
195195
expect(method).toBeCalledTimes(1)
196-
await new Promise(resolve => { setTimeout(resolve, PROMISE_TIMEOUT * 3) })
196+
await new Promise(resolve => {
197+
setTimeout(resolve, PROMISE_TIMEOUT * 3)
198+
})
197199
expect(method).toBeCalledTimes(2)
198-
await new Promise(resolve => { setTimeout(resolve, PROMISE_TIMEOUT * 3) })
200+
await new Promise(resolve => {
201+
setTimeout(resolve, PROMISE_TIMEOUT * 3)
202+
})
199203
expect(method).toBeCalledTimes(3)
200204
await instance.load()
201205
await instance.load()
202-
await new Promise(resolve => { setTimeout(resolve) })
206+
await new Promise(resolve => {
207+
setTimeout(resolve)
208+
})
203209
expect(method).toBeCalledTimes(4)
204210
await instance.load()
205211
await instance.load()
206212
await instance.load(true)
207-
await new Promise(resolve => { setTimeout(resolve) })
213+
await new Promise(resolve => {
214+
setTimeout(resolve)
215+
})
208216
expect(method).toBeCalledTimes(6)
209217
instance.setPollingInterval(PROMISE_TIMEOUT * 4)
210218
await instance.destroy()
211219
})
212220

221+
test('should create instance with polling and needPolling', async () => {
222+
const method = jest.fn(fakeSuccessPromise)
223+
const instance = new DataLoader({
224+
key: 'test',
225+
method,
226+
needPolling: () => true,
227+
pollingInterval: PROMISE_TIMEOUT * 2,
228+
})
229+
await instance.load()
230+
expect(method).toBeCalledTimes(1)
231+
await new Promise(resolve => {
232+
setTimeout(resolve, PROMISE_TIMEOUT * 3)
233+
})
234+
expect(method).toBeCalledTimes(2)
235+
await new Promise(resolve => {
236+
setTimeout(resolve, PROMISE_TIMEOUT * 3)
237+
})
238+
expect(method).toBeCalledTimes(3)
239+
await instance.load()
240+
await instance.load()
241+
await new Promise(resolve => {
242+
setTimeout(resolve)
243+
})
244+
expect(method).toBeCalledTimes(4)
245+
await instance.load()
246+
await instance.load()
247+
await instance.load(true)
248+
await new Promise(resolve => {
249+
setTimeout(resolve)
250+
})
251+
expect(method).toBeCalledTimes(6)
252+
instance.setPollingInterval(PROMISE_TIMEOUT * 4)
253+
await instance.destroy()
254+
})
255+
256+
test('should create instance with polling and needPolling that return false', async () => {
257+
const method = jest.fn(fakeSuccessPromise)
258+
const instance = new DataLoader({
259+
key: 'test',
260+
method,
261+
needPolling: () => false,
262+
pollingInterval: PROMISE_TIMEOUT * 2,
263+
})
264+
await instance.load()
265+
expect(method).toBeCalledTimes(1)
266+
await new Promise(resolve => {
267+
setTimeout(resolve, PROMISE_TIMEOUT * 3)
268+
})
269+
expect(method).toBeCalledTimes(1)
270+
instance.setNeedPolling(true)
271+
await instance.destroy()
272+
})
273+
213274
test('should update outdated data', async () => {
214275
const method = jest.fn(fakeSuccessPromise)
215276
const onSuccess = jest.fn()
@@ -222,12 +283,16 @@ describe('Dataloader class', () => {
222283
instance.addOnSuccessListener(onSuccess)
223284
expect(instance.status).toBe(StatusEnum.LOADING)
224285
expect(method).toBeCalledTimes(1)
225-
await new Promise(resolve => { setTimeout(resolve, PROMISE_TIMEOUT) })
286+
await new Promise(resolve => {
287+
setTimeout(resolve, PROMISE_TIMEOUT)
288+
})
226289
expect(onSuccess).toBeCalledTimes(1)
227290
await instance.load()
228291
expect(method).toBeCalledTimes(1)
229292
expect(onSuccess).toBeCalledTimes(1)
230-
await new Promise(resolve => { setTimeout(resolve, PROMISE_TIMEOUT * 2) })
293+
await new Promise(resolve => {
294+
setTimeout(resolve, PROMISE_TIMEOUT * 2)
295+
})
231296
await instance.load()
232297
expect(method).toBeCalledTimes(2)
233298
expect(onSuccess).toBeCalledTimes(2)
@@ -245,7 +310,9 @@ describe('Dataloader class', () => {
245310
expect(instance.status).toBe(StatusEnum.LOADING)
246311
}
247312
// Because wait for setTimeout tryLaunch in dataloader.ts
248-
await new Promise(resolve => { setTimeout(resolve) })
313+
await new Promise(resolve => {
314+
setTimeout(resolve)
315+
})
249316
expect(method).toBeCalledTimes(2)
250317
})
251318
})

packages/use-dataloader/src/__tests__/useDataLoader.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ describe('useDataLoader', () => {
150150
method: () =>
151151
new Promise(resolve => {
152152
setTimeout(() => resolve(null), PROMISE_TIMEOUT)
153-
})
153+
}),
154154
},
155155
wrapper,
156156
})
@@ -272,6 +272,7 @@ describe('useDataLoader', () => {
272272
test('should render correctly with pooling', async () => {
273273
const pollingProps = {
274274
config: {
275+
needPolling: () => true,
275276
pollingInterval: PROMISE_TIMEOUT,
276277
},
277278
key: 'test-6',

packages/use-dataloader/src/dataloader.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import { DEFAULT_MAX_CONCURRENT_REQUESTS, StatusEnum } from './constants'
2-
import { OnCancelFn, OnErrorFn, OnSuccessFn, PromiseType } from './types'
2+
import {
3+
NeedPollingType,
4+
OnCancelFn,
5+
OnErrorFn,
6+
OnSuccessFn,
7+
PromiseType,
8+
} from './types'
39

410
export type DataLoaderConstructorArgs<T = unknown> = {
511
enabled?: boolean
612
key: string
713
method: () => PromiseType<T>
814
pollingInterval?: number
15+
needPolling?: NeedPollingType<T>
916
maxDataLifetime?: number
1017
keepPreviousData?: boolean
1118
}
@@ -25,6 +32,8 @@ class DataLoader<T = unknown> {
2532

2633
public pollingInterval?: number
2734

35+
public needPolling: NeedPollingType<T> = true
36+
2837
public maxDataLifetime?: number
2938

3039
public isDataOutdated = false
@@ -61,6 +70,7 @@ class DataLoader<T = unknown> {
6170
this.pollingInterval = args?.pollingInterval
6271
this.keepPreviousData = args?.keepPreviousData
6372
this.maxDataLifetime = args.maxDataLifetime
73+
this.needPolling = args.needPolling ?? true
6474
if (args.enabled) {
6575
this.tryLaunch()
6676
} else {
@@ -146,7 +156,13 @@ class DataLoader<T = unknown> {
146156
}
147157
}
148158
DataLoader.started -= 1
149-
if (this.pollingInterval && !this.destroyed) {
159+
if (
160+
this.pollingInterval &&
161+
!this.destroyed &&
162+
(typeof this.needPolling === 'function'
163+
? this.needPolling(DataLoader.cachedData[this.key] as T)
164+
: this.needPolling)
165+
) {
150166
this.timeout = setTimeout(
151167
// eslint-disable-next-line @typescript-eslint/no-misused-promises
152168
this.launch,
@@ -217,6 +233,7 @@ class DataLoader<T = unknown> {
217233
}
218234

219235
public async destroy(): Promise<void> {
236+
DataLoader.cachedData[this.key] = undefined
220237
await this.cancel?.()
221238
if (this.timeout) {
222239
clearTimeout(this.timeout)
@@ -234,6 +251,10 @@ class DataLoader<T = unknown> {
234251
this.tryLaunch()
235252
}
236253
}
254+
255+
public setNeedPolling(needPolling: NeedPollingType<T>): void {
256+
this.needPolling = needPolling
257+
}
237258
}
238259

239260
export default DataLoader

packages/use-dataloader/src/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export type OnSuccessFn<T = unknown> =
77
| ((result: T) => void | Promise<void>)
88
| undefined
99
export type OnCancelFn = (() => void | Promise<void>) | undefined
10+
export type NeedPollingType<T = unknown> = boolean | ((data: T) => boolean)
1011

1112
/**
1213
* @typedef {Object} UseDataLoaderConfig
@@ -16,6 +17,7 @@ export type OnCancelFn = (() => void | Promise<void>) | undefined
1617
* @property {number} [pollingInterval] relaunch the request after the last success
1718
* @property {boolean} [enabled=true] launch request automatically (default true)
1819
* @property {boolean} [keepPreviousData=true] do we need to keep the previous data after reload (default true)
20+
* @property {NeedPollingType} [needPolling=true] When pollingInterval is set you can set a set a custom callback to know if polling is enabled
1921
*/
2022
export interface UseDataLoaderConfig<T = unknown> {
2123
enabled?: boolean
@@ -28,6 +30,7 @@ export interface UseDataLoaderConfig<T = unknown> {
2830
* Max time before data from previous success is considered as outdated (in millisecond)
2931
*/
3032
maxDataLifetime?: number
33+
needPolling?: NeedPollingType
3134
}
3235

3336
/**

packages/use-dataloader/src/useDataLoader.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const useDataLoader = <T>(
2828
onSuccess,
2929
pollingInterval,
3030
maxDataLifetime,
31+
needPolling,
3132
}: UseDataLoaderConfig<T> = {},
3233
): UseDataLoaderResult<T> => {
3334
const isMountedRef = useRef(false)
@@ -50,6 +51,7 @@ const useDataLoader = <T>(
5051
keepPreviousData,
5152
maxDataLifetime,
5253
method,
54+
needPolling,
5355
pollingInterval,
5456
}) as DataLoader<T>
5557

@@ -63,6 +65,7 @@ const useDataLoader = <T>(
6365
getOrAddRequest,
6466
maxDataLifetime,
6567
method,
68+
needPolling,
6669
pollingInterval,
6770
keepPreviousData,
6871
subscribeFn,
@@ -118,6 +121,12 @@ const useDataLoader = <T>(
118121
}
119122
}, [pollingInterval, request])
120123

124+
useEffect(() => {
125+
if (needPolling !== request.needPolling) {
126+
request.setNeedPolling(needPolling ?? true)
127+
}
128+
}, [needPolling, request])
129+
121130
useEffect(() => {
122131
isFetchingRef.current = isLoading || isPolling
123132
}, [isLoading, isPolling])

0 commit comments

Comments
 (0)