Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions packages/use-dataloader/src/DataLoaderProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ import { OnErrorFn, PromiseType } from './types'

type CachedData = Record<string, unknown>
type Reloads = Record<string, () => Promise<void | unknown>>
type Requests = Record<string, DataLoader>
type Requests = Record<string, DataLoader<unknown, unknown>>

type UseDataLoaderInitializerArgs<T = unknown> = {
method: () => PromiseType<T>
type UseDataLoaderInitializerArgs<ResultType = unknown> = {
method: () => PromiseType<ResultType>
/**
* Max time before data from previous success is considered as outdated (in millisecond)
*/
Expand All @@ -38,19 +38,24 @@ type GetReloadsFn = {
(key?: string): (() => Promise<void | unknown>) | undefined
}

export interface IDataLoaderContext {
addRequest: (key: string, args: UseDataLoaderInitializerArgs) => DataLoader
getOrAddRequest: <T>(
export type IDataLoaderContext = {
addRequest: <ResultType, ErrorType>(
key: string,
args: UseDataLoaderInitializerArgs<T>,
) => DataLoader<T>
args: UseDataLoaderInitializerArgs<ResultType>,
) => DataLoader<ResultType, ErrorType>
getOrAddRequest: <ResultType, ErrorType>(
key: string,
args: UseDataLoaderInitializerArgs<ResultType>,
) => DataLoader<ResultType, ErrorType>
cacheKeyPrefix?: string
onError?: (error: Error) => void | Promise<void>
clearAllCachedData: () => void
clearCachedData: (key: string) => void
getCachedData: GetCachedDataFn
getReloads: GetReloadsFn
getRequest: (key: string) => DataLoader
getRequest: <ResultType, ErrorType>(
key: string,
) => DataLoader<ResultType, ErrorType>
reload: (key?: string) => Promise<void>
reloadAll: () => Promise<void>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { UseDataLoaderConfig } from '../types'
import useDataLoader from '../useDataLoader'

type UseDataLoaderHookProps = {
config: UseDataLoaderConfig<unknown>
config: UseDataLoaderConfig<unknown, unknown>
key: string
method: () => Promise<unknown>
children?: ReactNode
Expand Down
6 changes: 3 additions & 3 deletions packages/use-dataloader/src/dataloader.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { DEFAULT_MAX_CONCURRENT_REQUESTS, StatusEnum } from './constants'
import { PromiseType } from './types'

export type DataLoaderConstructorArgs<T = unknown> = {
export type DataLoaderConstructorArgs<ResultType> = {
key: string
method: () => PromiseType<T>
method: () => PromiseType<ResultType>
enabled?: boolean
notifyChanges?: () => void
}

class DataLoader<ResultType = unknown, ErrorType = unknown> {
class DataLoader<ResultType, ErrorType> {
public static maxConcurrent = DEFAULT_MAX_CONCURRENT_REQUESTS

public static started = 0
Expand Down
40 changes: 22 additions & 18 deletions packages/use-dataloader/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ export class PromiseType<T = unknown> extends Promise<T> {
public cancel?: () => void
}

export type OnErrorFn = ((err: Error) => void | Promise<void>) | undefined
export type OnSuccessFn<T = unknown> =
| ((result: T) => void | Promise<void>)
export type OnErrorFn<ErrorType = Error> =
| ((err: ErrorType) => void | Promise<void>)
| undefined
export type OnSuccessFn<ResultType> =
| ((result: ResultType) => void | Promise<void>)
| undefined
export type OnCancelFn = (() => void | Promise<void>) | undefined
export type NeedPollingType<T = unknown> = boolean | ((data?: T) => boolean)
export type NeedPollingType<ResultType> =
| boolean
| ((data?: ResultType) => boolean)

/**
* @typedef {Object} UseDataLoaderConfig
Expand All @@ -20,15 +24,15 @@ export type NeedPollingType<T = unknown> = boolean | ((data?: T) => boolean)
* @property {number} [dataLifetime=undefined] Time before data from previous success is considered as outdated (in millisecond)
* @property {NeedPollingType} [needPolling=true] When pollingInterval is set you can set a set a custom callback to know if polling is enabled
*/
export interface UseDataLoaderConfig<T = unknown> {
export interface UseDataLoaderConfig<ResultType, ErrorType> {
enabled?: boolean
initialData?: T
initialData?: ResultType
keepPreviousData?: boolean
onError?: OnErrorFn
onSuccess?: OnSuccessFn
onError?: OnErrorFn<ErrorType>
onSuccess?: OnSuccessFn<ResultType>
pollingInterval?: number
dataLifetime?: number
needPolling?: NeedPollingType<T>
needPolling?: NeedPollingType<ResultType>
}

/**
Expand All @@ -43,15 +47,15 @@ export interface UseDataLoaderConfig<T = unknown> {
* @property {string} error the error occured during the request
* @property {Function} reload reload the data
*/
export interface UseDataLoaderResult<T = unknown, ErrorType = Error> {
data?: T
export interface UseDataLoaderResult<ResultType, ErrorType> {
data?: ResultType
error?: ErrorType
isError: boolean
isIdle: boolean
isLoading: boolean
isPolling: boolean
isSuccess: boolean
previousData?: T
previousData?: ResultType
reload: () => Promise<void>
}

Expand All @@ -63,16 +67,16 @@ export type UsePaginatedDataLoaderMethodParams = {
perPage: number
}

export type UsePaginatedDataLoaderConfig<T = unknown> =
UseDataLoaderConfig<T> & {
export type UsePaginatedDataLoaderConfig<ResultType, ErrorType> =
UseDataLoaderConfig<ResultType, ErrorType> & {
initialPage?: number
perPage?: number
}

export type UsePaginatedDataLoaderResult<T = unknown> = {
pageData?: T
data?: Record<number, T | undefined>
error?: Error
export type UsePaginatedDataLoaderResult<ResultType, ErrorType> = {
pageData?: ResultType
data?: Record<number, ResultType | undefined>
error?: ErrorType
isError: boolean
isIdle: boolean
isLoading: boolean
Expand Down
9 changes: 4 additions & 5 deletions packages/use-dataloader/src/useDataLoader.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useDataLoaderContext } from './DataLoaderProvider'
import { StatusEnum } from './constants'
import DataLoader from './dataloader'
import { PromiseType, UseDataLoaderConfig, UseDataLoaderResult } from './types'

function useDataLoader<ResultType, ErrorType = Error>(
function useDataLoader<ResultType = unknown, ErrorType = Error>(
fetchKey: string,
method: () => PromiseType<ResultType>,
{
Expand All @@ -16,7 +15,7 @@ function useDataLoader<ResultType, ErrorType = Error>(
pollingInterval,
initialData,
dataLifetime,
}: UseDataLoaderConfig<ResultType> = {},
}: UseDataLoaderConfig<ResultType, ErrorType> = {},
): UseDataLoaderResult<ResultType, ErrorType> {
const { getOrAddRequest, onError: onGlobalError } = useDataLoaderContext()
const methodRef = useRef(method)
Expand All @@ -28,10 +27,10 @@ function useDataLoader<ResultType, ErrorType = Error>(
setCounter(current => current + 1)
}, [])

const request = getOrAddRequest(fetchKey, {
const request = getOrAddRequest<ResultType, ErrorType>(fetchKey, {
enabled,
method: methodRef.current,
}) as DataLoader<ResultType, ErrorType>
})

useEffect(() => {
request.addObserver(forceRerender)
Expand Down
12 changes: 7 additions & 5 deletions packages/use-dataloader/src/usePaginatedDataLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ import useDataLoader from './useDataLoader'
* @param {useDataLoaderConfig} config hook configuration
* @returns {useDataLoaderResult} hook result containing data, request state, and method to reload the data
*/
const usePaginatedDataLoader = <T>(
const usePaginatedDataLoader = <ResultType, ErrorType>(
baseFetchKey: string,
method: (params: UsePaginatedDataLoaderMethodParams) => PromiseType<T>,
method: (
params: UsePaginatedDataLoaderMethodParams,
) => PromiseType<ResultType>,
{
enabled = true,
initialData,
Expand All @@ -28,13 +30,13 @@ const usePaginatedDataLoader = <T>(
needPolling,
initialPage,
perPage = 1,
}: UsePaginatedDataLoaderConfig<T> = {},
): UsePaginatedDataLoaderResult<T> => {
}: UsePaginatedDataLoaderConfig<ResultType, ErrorType> = {},
): UsePaginatedDataLoaderResult<ResultType, ErrorType> => {
if (typeof baseFetchKey !== 'string') {
throw new Error(KEY_IS_NOT_STRING_ERROR)
}

const [data, setData] = useState<Record<number, T | undefined>>({})
const [data, setData] = useState<Record<number, ResultType | undefined>>({})
const [page, setPage] = useState<number>(initialPage ?? 1)

const pageMethod = useCallback(
Expand Down