-
Notifications
You must be signed in to change notification settings - Fork 19
/
query.ts
36 lines (27 loc) · 1.19 KB
/
query.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import { useEffect, useMemo, useRef, useState } from 'react'
export type Query<T> = { loading: boolean; error: boolean; data?: T }
type Options = { enabled?: boolean; cacheTime?: number }
const defaultOptions: Options = { enabled: true, cacheTime: 0 }
const cache = new Map<string, unknown>()
export const useQuery = <T>(key: string | string[], fetcher: () => Promise<T>, options?: Options): Query<T> => {
const ref = useRef<() => Promise<T>>()
const [status, setStatus] = useState<'stale' | 'loading' | 'error' | 'done'>('stale')
const [data, setData] = useState<T>()
const id = useMemo(() => (typeof key === 'string' ? key : key.join('/')), [key])
const { enabled, cacheTime } = { ...defaultOptions, ...options }
ref.current = fetcher
useEffect(() => {
if (enabled) {
if (cacheTime === Infinity && cache.has(id)) {
setData(cache.get(id) as T)
} else {
setStatus('loading')
ref
.current?.()
.then((data) => (setData(data), setStatus('done'), cache.set(id, data)))
.catch(() => setStatus('error'))
}
}
}, [id, enabled, cacheTime])
return { loading: status === 'loading', error: status === 'error', data }
}