97 lines
2.7 KiB
TypeScript
97 lines
2.7 KiB
TypeScript
import { createContext, useContext, useEffect, useState } from 'react';
|
|
import { useCache } from './cache';
|
|
import type { WidgetProps } from './index';
|
|
|
|
type UseQueryOptions = {
|
|
cache: boolean;
|
|
cacheKey?: string;
|
|
};
|
|
|
|
export function useQuery<Result>(endpoint: string | undefined, options: UseQueryOptions = { cache: true }) {
|
|
const [cache, setCache] = useCache();
|
|
|
|
const [result, setResult] = useState<Result | undefined>(undefined);
|
|
const [fetching, setFetching] = useState(false);
|
|
const [error, setError] = useState<Error | undefined>(undefined);
|
|
|
|
useEffect(() => {
|
|
let abortController: AbortController | undefined = undefined;
|
|
|
|
async function handleQuery() {
|
|
if (typeof endpoint === 'undefined') {
|
|
return;
|
|
}
|
|
|
|
const cacheKey = options.cacheKey ?? endpoint;
|
|
if (options.cache && cacheKey in cache._query) {
|
|
setResult(cache._query[cacheKey] as Result);
|
|
return;
|
|
}
|
|
|
|
abortController = new AbortController();
|
|
setFetching(true);
|
|
try {
|
|
const url = new URL(endpoint, `${window.location.protocol}//${window.location.host}`);
|
|
|
|
const response = await fetch(url.href, { signal: abortController.signal });
|
|
if (!response.ok || response.status !== 200) {
|
|
throw new Error('Failed to fetch sections');
|
|
}
|
|
|
|
const data = await response.json();
|
|
setResult(data);
|
|
setError(undefined);
|
|
if (cache) {
|
|
setCache(prev => ({
|
|
...prev,
|
|
_query: {
|
|
...prev._query,
|
|
[cacheKey]: data,
|
|
},
|
|
}));
|
|
}
|
|
} catch (e: unknown) {
|
|
if (!(e instanceof Error)) {
|
|
e = new Error(String(e));
|
|
}
|
|
|
|
if ((e as Error).name === 'AbortError') {
|
|
return;
|
|
}
|
|
|
|
setError(e as Error);
|
|
console.error(e);
|
|
} finally {
|
|
setFetching(false);
|
|
}
|
|
}
|
|
|
|
void handleQuery();
|
|
|
|
return () => {
|
|
if (abortController) {
|
|
abortController.abort();
|
|
}
|
|
};
|
|
}, [endpoint]);
|
|
|
|
function clear() {
|
|
setResult(undefined);
|
|
setFetching(false);
|
|
setError(undefined);
|
|
}
|
|
|
|
return { data: result, isFetching: fetching, error, clear };
|
|
}
|
|
|
|
type WidgetPropsContextValue = WidgetProps & {
|
|
hide: () => void;
|
|
show: () => void;
|
|
};
|
|
|
|
const WidgetPropsContext = createContext<WidgetPropsContextValue | undefined>(undefined);
|
|
export const WidgetPropsProvider = WidgetPropsContext.Provider;
|
|
export function useWidgetProps() {
|
|
return useContext(WidgetPropsContext) as WidgetPropsContextValue;
|
|
}
|