Created
June 18, 2024 05:57
-
-
Save enpitsuLin/b4f70e8d4b83cfe37f253cc7f95292ea to your computer and use it in GitHub Desktop.
simple react-query
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
const __internalStore = new Map<string, any>() | |
const __listeners = new Set<() => void>() | |
const cache = { | |
set(key: string, value: any) { | |
__internalStore.set(key, value); | |
__listeners.forEach(listener => listener()); | |
}, | |
delete(key: string) { | |
__internalStore.delete(key); | |
__listeners.forEach(listener => listener()); | |
}, | |
subscribe(listener: () => void) { | |
__listeners.add(listener); | |
return () => __listeners.delete(listener); | |
}, | |
get<T>(key: string): T { | |
return __internalStore.get(key); | |
} | |
} | |
interface QueryOptions<Result = any> { | |
queryKey: unknown[], | |
queryFn: () => Promise<Result> | |
initialData?: Result | |
} | |
interface UseQueryReturnBase<Result = any> { | |
isPending: boolean | |
isError: boolean, | |
data: Result | null | |
error: unknown | null | |
refresh: () => void | |
} | |
interface UseQueryReturnPending<Result = any> extends UseQueryReturnBase<Result> { | |
isPending: true | |
isError: false, | |
data: Result | null | |
error: null | |
refresh: () => void | |
} | |
interface UseQueryReturnSuccess<Result = any> extends UseQueryReturnBase<Result> { | |
isPending: false | |
isError: false | |
data: Result | |
error: null | |
refresh: () => void | |
} | |
interface UseQueryReturnError<Result = any> extends UseQueryReturnBase<Result> { | |
isPending: false | |
isError: true, | |
data: Result | null | |
error: unknown | |
refresh: () => void | |
} | |
type UseQueryReturn<Result = any> = UseQueryReturnPending<Result> | UseQueryReturnSuccess<Result> | UseQueryReturnError<Result> | |
function useQuery<Result = any>(options: QueryOptions<Result>): UseQueryReturn<Result> { | |
const _options = useMemo(() => options, [options.queryKey]) | |
const { | |
queryKey, | |
queryFn, | |
initialData: defaultData | |
} = _options | |
const key = queryKey.map(i => JSON.stringify(i)).join(':') | |
const data = useSyncExternalStore( | |
cache.subscribe, | |
useCallback( | |
() => cache.get<Result>(key) ?? defaultData ?? null, | |
[_options] | |
) | |
); | |
const [isPending, setIsPending] = useState(false) | |
const [error, setError] = useState<unknown | null>(null) | |
const isCancelled = useRef(false) | |
function refresh() { | |
isCancelled.current = false; | |
setIsPending(true); | |
queryFn() | |
.then(data => { | |
if (!isCancelled.current) { | |
cache.set(key, data); | |
setIsPending(false); | |
} | |
}) | |
.catch(err => { | |
setError(err) | |
}) | |
.finally(() => { | |
cleanupEffect() | |
}); | |
} | |
function cleanupEffect() { | |
isCancelled.current = true; | |
setIsPending(false); | |
} | |
useEffect(() => { | |
refresh() | |
return cleanupEffect | |
}, [key]) | |
return { | |
isPending, | |
isError: !!error, | |
data, | |
error, | |
refresh | |
} as UseQueryReturn<Result> | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment