Created
November 29, 2022 20:06
-
-
Save docentedev/7872925214bbf99b09873623825a0dd8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable @typescript-eslint/no-explicit-any */ | |
import { AxiosRequestConfig, AxiosResponseHeaders } from 'axios'; | |
import { useEffect, useState } from 'react'; | |
import updateIfNewData from '../../utils/updateIfNewData'; | |
export type InitialState<T> = { | |
isLoading: boolean; | |
data: T | null; | |
error: any; | |
isError: boolean | null; | |
isSuccess: boolean | null; | |
extra?: any; | |
params?: any; | |
}; | |
export const initialState = { | |
isLoading: false, | |
data: null, | |
error: null, | |
isError: null, | |
isSuccess: null, | |
extra: null, | |
}; | |
export interface AxiosResponseCompare<T = any, D = any> { | |
data: T; | |
compare: any; | |
extra?: any; | |
status: number; | |
statusText: string; | |
headers: AxiosResponseHeaders; | |
config: AxiosRequestConfig<D>; | |
request?: any; | |
} | |
type Options = { | |
timeout: number; | |
debug: boolean; | |
}; | |
const defaultOptions: Options = { timeout: 0, debug: false }; | |
const tryUpdate = updateIfNewData(); | |
export function useQuery<T, P = any>( | |
key: string, | |
callback: (params: P) => Promise<any>, | |
options: Partial<Options> = defaultOptions, | |
) { | |
let lastParams: any = null; | |
let interval: any = null; | |
const [init, setInit] = useState<boolean>(false); | |
const [state, setState] = useState<InitialState<T>>(initialState); | |
const log = (...args: unknown[]) => | |
options.debug && console.log(key, ...args); | |
async function run(params?: P, prevData = null): Promise<any> { | |
lastParams = params; | |
const messages: any[] = []; | |
const now = Date.now(); | |
messages.push({ action: 'request', params, now }); | |
setState((s) => ({ ...s, isLoading: true })); | |
try { | |
let response: any = null; | |
if (!prevData) { | |
response = await callback(params || (undefined as any)); | |
} else { | |
response = prevData; | |
} | |
messages.push({ | |
action: 'response', | |
response, | |
dt: Date.now() - now, | |
}); | |
setState((s) => ({ | |
...s, | |
data: response.data, | |
isLoading: false, | |
isError: false, | |
isSuccess: true, | |
extra: response.extra, | |
params, | |
})); | |
return response.data; | |
} catch (error) { | |
messages.push({ action: 'error', error, dt: Date.now() - now }); | |
setState((s) => ({ | |
...s, | |
error, | |
isLoading: false, | |
isError: true, | |
isSuccess: false, | |
params, | |
})); | |
return Promise.reject(error); | |
} finally { | |
setInit(true); | |
log(messages); | |
} | |
} | |
useEffect(() => { | |
if (options.timeout && options.timeout > 0 && init) { | |
interval = setInterval(async () => { | |
if (!state.isLoading && state.isSuccess) { | |
callback(lastParams || (undefined as any)).then( | |
(response) => { | |
let compare = null; | |
if (response.compare) compare = response.compare; | |
else compare = response.data; | |
if (compare) { | |
tryUpdate( | |
`useQuery__${key}`, | |
compare, | |
() => { | |
run(lastParams, response); | |
}, | |
options.debug, | |
); | |
} | |
}, | |
); | |
} | |
}, options.timeout); | |
} | |
return () => clearInterval(interval); | |
}, [options.timeout, init, state]); | |
return { | |
...state, | |
run, | |
}; | |
} | |
export default useQuery; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment