From 6676f277a006953c48c3de5cb56c2bc6af8f0120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= <2pi_r2@gmx.de> Date: Mon, 14 Feb 2022 15:05:52 +0100 Subject: [PATCH 1/3] abort fetch! --- verwaltung/composables/auth/sse.ts | 15 +++- verwaltung/composables/reloadableData.ts | 88 +++++++++++++++++++----- verwaltung/pages/user/[id].vue | 40 +++++------ verwaltung/pages/user/index.vue | 11 ++- vite-plugins/server-router.ts | 3 +- 5 files changed, 107 insertions(+), 50 deletions(-) diff --git a/verwaltung/composables/auth/sse.ts b/verwaltung/composables/auth/sse.ts index 76e6e04..17e6538 100644 --- a/verwaltung/composables/auth/sse.ts +++ b/verwaltung/composables/auth/sse.ts @@ -5,7 +5,11 @@ const invalidationCb: Record void)[]> = {}; const { authToken } = useAuthData(); -export function onInvalidate(key: string[], cb: () => void) { +export function onInvalidate( + key: string[], + cb: () => void, + signal?: AbortSignal +) { if (currentSource?.readyState === 2) { createNewEventSource(); } @@ -18,10 +22,17 @@ export function onInvalidate(key: string[], cb: () => void) { } }); - onScopeDispose(() => { + const cleanup = () => { key.forEach(k => { invalidationCb[k].splice(invalidationCb[k].indexOf(cb), 1); }); + }; + + signal?.addEventListener('abort', cleanup); + + onScopeDispose(() => { + cleanup(); + signal?.removeEventListener('abort', cleanup); }); } let currentSource: EventSource | null = null; diff --git a/verwaltung/composables/reloadableData.ts b/verwaltung/composables/reloadableData.ts index 4f49104..93c4550 100644 --- a/verwaltung/composables/reloadableData.ts +++ b/verwaltung/composables/reloadableData.ts @@ -1,38 +1,92 @@ -import { ref, onMounted, Ref, watchEffect } from 'vue'; +import { + ref, + Ref, + shallowRef, + shallowReadonly, + DeepReadonly, + watch, + isRef, + ShallowRef, + onScopeDispose +} from 'vue'; +import { onInvalidate } from '@/composables/auth'; -export function useDataReload(cb: () => Promise, init?: T) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const data: Ref = ref() as any; +export function useDataReload( + cb: (options: O) => Promise, + args: Ref | null, + { + init, + watchArgs = true, + invalidations = [] + }: { + init?: T; + watchArgs?: boolean; + invalidations?: Ref> | Array; + } = {} +) { + // Init Refs const loading = ref(true); - const error = ref(null); + const data = shallowRef() as unknown as ShallowRef; + const error = shallowRef(null); if (init) data.value = init; - async function reload(): Promise { + let fetchAbort: AbortController; + + async function reload() { + if (fetchAbort) fetchAbort.abort(); + + fetchAbort = new AbortController(); + + // Stop fetch on unmounted etc. + onScopeDispose(() => fetchAbort.abort()); + try { - const val = await cb(); + // any wird hier benötigt da signal eigentlich typenmäßig nicht existiert aber praktisch doch. + const val = await cb({ + signal: fetchAbort.signal, + ...(args?.value ?? {}) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any); data.value = val; error.value = null; loading.value = false; - return val; } catch (ex) { loading.value = false; error.value = ex as Error; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return null as any; } } - onMounted(() => watchEffect(() => reload())); + // Watch argument change? + if (watchArgs && args) watch(args, reload); + + if (isRef(invalidations)) { + // watch invalidations + let invalidateAbort = new AbortController(); + + onInvalidate(invalidations.value, reload, invalidateAbort.signal); + + onScopeDispose(() => invalidateAbort.abort()); + + watch(invalidations, () => { + invalidateAbort.abort(); + invalidateAbort = new AbortController(); + onInvalidate(invalidations.value, reload, invalidateAbort.signal); + }); + } else { + // Static invalidations + onInvalidate(invalidations, reload); + } + + // Load data! + reload(); return { - data, + // Typenmäßig machen wir den return nur readable um bugs zu vermeiden. Wir machen daher auch nur ein shallow da die Datenstrucktur nicht komplett proxifiziert werden muss. + // Da der .value wert readonly sein sollte nutzen wir shallowReadonly. + data: shallowReadonly(data as Ref>), reload, loading, - error, - nav: () => { - reload(); - return true; - } + error }; } diff --git a/verwaltung/pages/user/[id].vue b/verwaltung/pages/user/[id].vue index ac25d13..250c718 100644 --- a/verwaltung/pages/user/[id].vue +++ b/verwaltung/pages/user/[id].vue @@ -1,8 +1,7 @@