Skip to content

Instantly share code, notes, and snippets.

@sorrycc
Last active May 21, 2022 03:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sorrycc/74024efed3284121ae7dce0a3fa57bdf to your computer and use it in GitHub Desktop.
Save sorrycc/74024efed3284121ae7dce0a3fa57bdf to your computer and use it in GitHub Desktop.
import { useCallback, useEffect, useSyncExternalStore } from 'react';
const defaultFetcher = (url: string) => {
return fetch(url).then((r) => r.json());
};
const cache = createCache(new Map());
initFocus(() => {
for (const key in EVENT_REVALIDATORS) {
if (EVENT_REVALIDATORS[key][0]) {
EVENT_REVALIDATORS[key][0]();
}
}
});
const FETCH: Record<string, any> = {};
const EVENT_REVALIDATORS: Record<string, Function[]> = {};
export function useSWR(key: string, fetcher?: Function) {
const fetcherRef = fetcher || defaultFetcher;
const getSnapshot = useCallback(() => {
return cache.provider.get(key);
}, [key]);
const subscribe = useCallback(
(callback: () => void) => {
return cache.subscribe(key, (current: any) => {
callback();
});
},
[key],
);
const cached = useSyncExternalStore(subscribe, getSnapshot);
const revalidate = useCallback(
async (opts?: { dedupe?: boolean }) => {
try {
if (!FETCH[key] || !opts?.dedupe) {
FETCH[key] = fetcherRef(key);
}
cache.setter(key, {
data: await FETCH[key],
});
} catch (e) {
cache.setter(key, {
error: e,
});
}
},
[key],
);
const softRevalidate = revalidate.bind(undefined, { dedupe: true });
const onRevalidate = () => {
revalidate().finally(() => {});
};
const unsubEvents = subscribeCallback(key, EVENT_REVALIDATORS, onRevalidate);
useEffect(() => {
softRevalidate().finally(() => {});
return () => {
unsubEvents();
};
}, []);
useEffect(() => {}, []);
return {
data: cached?.data,
error: cached?.error,
revalidate,
};
}
// for DEBUG
useSWR.local = true;
function createCache(provider: any) {
const subscriptions: Record<string, any> = {};
const subscribe = (key: string, callback: any) => {
const subs = (subscriptions[key] ||= []);
subs.push(callback);
return () => {
subs.splice(subs.indexOf(callback), 1);
};
};
const setter = (key: string, value: any) => {
provider.set(key, value);
const subs = subscriptions[key];
if (subs) {
for (let i = subs.length; i--; ) {
subs[i](value);
}
}
};
return {
provider,
subscribe,
setter,
};
}
function subscribeCallback(
key: string,
callbacks: Record<string, Function[]>,
callback: Function,
) {
callbacks[key] ||= [];
callbacks[key].push(callback);
return () => {
callbacks[key].splice(callbacks[key].indexOf(callback), 1);
};
}
function initFocus(callback: () => void) {
document.addEventListener('visibilitychange', callback);
window.addEventListener('focus', callback);
return () => {
document.removeEventListener('visibilitychange', callback);
window.removeEventListener('focus', callback);
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment