Skip to content

Instantly share code, notes, and snippets.

@docentedev
Created November 29, 2022 20:06
Show Gist options
  • Save docentedev/7872925214bbf99b09873623825a0dd8 to your computer and use it in GitHub Desktop.
Save docentedev/7872925214bbf99b09873623825a0dd8 to your computer and use it in GitHub Desktop.
/* 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