UNPKG

@apparently/query

Version:

Simple and small data fetching library for SolidJS. Inspired by SWR.

128 lines (127 loc) 4.68 kB
import { createContext, onMount, onCleanup, useContext, createResource } from "solid-js"; import { ReactiveMap } from "@solid-primitives/map"; // @ts-ignore import stableHash from "stable-hash"; const QueryContext = createContext(); class ReactiveMapWithStableHash extends ReactiveMap { set(key, value) { if (stableHash(super.get(key)) === stableHash(value)) { return this; } return super.set(key, value); } } // TODO: // - deduplikace // - infiniteQuery (v postupu) // - error retry // - isRefething // - refetch on reconnect export function useQueryContext() { return useContext(QueryContext); } export const QueryConfig = (props) => { const cache = new ReactiveMapWithStableHash(); // Tady možná je trochu overheat, že has atd je reaktivní. return (<QueryContext.Provider value={{ fetcher: props.fetcher, cache }}> {props.children} </QueryContext.Provider>); }; export function useCache() { const { cache } = useQueryContext(); return cache; } export function useMutate() { const { fetcher, cache } = useQueryContext(); return async (key, data) => { cache.set(key, data ? data : await fetcher(key)); }; } export function useQuery(getKey, initialValue) { const { fetcher, cache } = useQueryContext(); const globalMutate = useMutate(); const mutate = (data) => globalMutate(getKey(), data); const refetch = () => mutate(); const cacheStorage = (initialValue) => { const key = getKey(); if (!cache.has(key) && initialValue) { cache.set(key, initialValue); } return [ () => cache.get(key), (newValue) => { cache.set(key, newValue()); return cache.get(key); } ]; }; const cacheFetcher = async (key) => { if (cache.has(key)) { (async () => cache.set(key, await fetcher(key)))(); return cache.get(key); } return await fetcher(key); }; // @ts-ignore // TODO: opravit type problem. const [resource] = createResource(getKey, cacheFetcher, { storage: cacheStorage, initialValue }); if (typeof window !== "undefined") { onMount(() => window.addEventListener("focus", refetch)); onCleanup(() => window.removeEventListener("focus", refetch)); } return [resource, { mutate }]; } // TODO: caching, suspense, klasický nekonečný scroll (bez deloadingu), vyčistit kód... //type UseInfiniteQueryGetKey<T> = (pageIndex: number, previousPageData: T | null) => string; //type UseInfiniteQuery<T> = [InitializedResource<T[][]>, { setSize: Setter<number>, size: Accessor<number>, loadingNextPage: Accessor<boolean> }]; // export function useInfiniteQuery<T>(getKey: UseInfiniteQueryGetKey<T>, initialValue?: T[]) { // const { fetcher, cache } = useQueryContext(); // const [size, setSize] = createSignal(0); // const [loadingPage, setLoadingPage] = createSignal(false); // const [data, setData] = createSignal<T[]>([]); // let max = 0; // onMount(async () => setData(await fetcher(getKey(0, null)))); // createEffect(() => { // if (data().length > max) { // max = data().length; // } // const _size = untrack(size); // console.log("Max loaded sofar:", max, "Size:", _size); // }); // async function forward() { // if (loadingPage()) { // return; // } // setLoadingPage(true); // let _data = untrack(data); // const newSize = size() + 1; // const fetchedData = await fetcher(getKey(newSize, null)); // let newData = [..._data, ...fetchedData]; // if (newSize % 4 == 0) { // newData = newData.slice(newData.length - 200, newData.length); // } // setData(newData); // setSize(newSize); // setLoadingPage(false); // } // async function back() { // if (loadingPage()) { // return; // } // if (size() <= 3) { // if (size() != 0) { // setSize(0); // } // return; // } // setLoadingPage(true); // let _data = untrack(data); // const newSize = size() - 1; // const fetchedData = await fetcher(getKey(newSize - 3, null)); // let newData = [...fetchedData, ..._data]; // if (newSize % 4 == 0) { // newData = newData.slice(0, newData.length - 200); // } // setData(newData); // setSize(newSize); // setLoadingPage(false); // } // return [data, { back, forward }]; // }