Skip to content

Commit

Permalink
feat(vue): reactive params (#387)
Browse files Browse the repository at this point in the history
  • Loading branch information
nmathew98 committed May 14, 2024
1 parent 966b3f2 commit e68ab8d
Showing 1 changed file with 110 additions and 82 deletions.
192 changes: 110 additions & 82 deletions packages/vue/src/use-qwery/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { computed, onMounted, onUnmounted, shallowRef } from "vue";
import { computed, isRef, onUnmounted, ref, shallowRef, watch } from "vue";
import type { Dispatch, Serializable } from "@b.s/incremental";
import { useQweryContext } from "../context";
import { useRememberScroll } from "../use-remember-scroll";
Expand All @@ -21,24 +21,28 @@ const BROADCASTING = new Set();
export const useQwery = <
I extends InitialValue,
S extends boolean | undefined = false,
>({
fetchPolicy = "cache-first",
queryKey,
initialValue,
placeholder,
onChange = noop,
onSuccess = noop,
onError = noop,
subscribe,
debug = false,
refetchOnWindowFocus = false,
broadcast = false,
suspense = false,
enabled = true,
affects,
requestManager,
throwErrors = suspense,
}: UseQweryOptions<I>): MaybePromise<S, UseQweryReturnVue<I>> => {
>(
options: UseQweryOptions<I>,
): MaybePromise<S, UseQweryReturnVue<I>> => {
const {
fetchPolicy = "cache-first",
queryKey,
initialValue,
placeholder,
onChange = noop,
onSuccess = noop,
onError = noop,
subscribe,
debug = false,
refetchOnWindowFocus = false,
broadcast = false,
suspense = false,
enabled = true,
affects,
requestManager,
throwErrors = suspense,
} = options;

const { cache, requestManager: globalRequestManager } =
useQweryContext() ?? Object.create(null);
const abortController = new AbortController();
Expand All @@ -47,18 +51,29 @@ export const useQwery = <
let isDispatchDisabled = false;

const qwery = shallowRef<ReturnType<typeof createQwery> | null>(null);
const rerenders = ref(0);
const result = computed(() => ({
...qwery.value,
rerenders: rerenders.value,
}));

useRememberScroll();

// With Vue, things behave like in React, ref reference
// changes and it causes a rerender
const rerender = () => {
qweryPromise.then(result => {
qwery.value = { ...(result ?? Object.create(null)) };
});
if (!qwery.value) {
return;
}

rerenders.value++;
};

let externalResolve;
const pending = new Promise(resolve => {
externalResolve = resolve;
});

const onCreateQwery = () => {
externalResolve();
if (
queryKey !== null &&
queryKey !== undefined &&
Expand All @@ -75,6 +90,8 @@ export const useQwery = <
};
const create = async () => {
if (!enabled) {
qwery.value = null;

return;
}

Expand All @@ -99,6 +116,7 @@ export const useQwery = <
isDispatchDisabled = true;
}

// TODO: map reactive
const _qwery = createQwery({
fetchPolicy,
queryKey,
Expand Down Expand Up @@ -138,62 +156,77 @@ export const useQwery = <
return _qwery;
};

const qweryPromise = create();
const watchSources = Object.values(options).map(value =>
isRef(value) ? value : () => value,
);

const subscription = async () => {
const awaitedQwery = (await qweryPromise) as ReturnType<
typeof createQwery
>;
watch(watchSources, create, { immediate: true });

return subscribeQwery(awaitedQwery, subscribe);
};
const unsubscribePromise = subscription();
watch(
qwery,
(qwery, _, onCleanup) => {
if (!qwery) {
return;
}

const unsubscribe = subscribeQwery(qwery, subscribe);

if (unsubscribe) {
onCleanup(unsubscribe);
}
},
{ immediate: true },
);

onUnmounted(() => {
BROADCASTING.delete(queryKey?.toString());
abortController.abort();
unsubscribePromise.then(unsubscribe => unsubscribe?.());
});

const onBroadcast = makeOnBroadcast(qweryPromise, {
fetchPolicy,
queryKey,
cache,
rerender,
});

onMounted(() => {
qweryPromise.then(qwery => {
void qwery?.channel?.addEventListener("message", onBroadcast);
});
});
watch(
qwery,
(qwery, _, onCleanup) => {
if (!qwery) {
return;
}

onUnmounted(() => {
qweryPromise.then(qwery => {
void qwery?.channel?.removeEventListener("message", onBroadcast);
});
});
const onBroadcast = makeOnBroadcast(qwery, {
fetchPolicy,
queryKey,
cache,
rerender,
});

const onWindowFocus = makeOnWindowFocus(qweryPromise);
qwery.channel?.addEventListener("message", onBroadcast);

onMounted(() => {
if (!refetchOnWindowFocus) {
return;
}
return onCleanup(() => {
qwery.channel?.removeEventListener("message", onBroadcast);
});
},
{ immediate: true },
);

watch(
qwery,
(qwery, _, onCleanup) => {
if (
!qwery ||
!refetchOnWindowFocus ||
(isBroadcasting(queryKey) && isMutating())
) {
return;
}

window.addEventListener("focus", onWindowFocus);
});
const onWindowFocus = makeOnWindowFocus(qwery);

onUnmounted(() => {
if (
!refetchOnWindowFocus ||
(isBroadcasting(queryKey) && isMutating())
) {
return;
}
window.addEventListener("focus", onWindowFocus);

window.removeEventListener("focus", onWindowFocus);
});
return onCleanup(() => {
window.removeEventListener("focus", onWindowFocus);
});
},
{ immediate: true },
);

const disabledDispatch = () => {
console.trace(
Expand All @@ -208,41 +241,37 @@ export const useQwery = <
return disabledDispatch();
}

if (!qwery.value?.crdt?.dispatch) {
if (!result.value?.crdt?.dispatch) {
return noop();
}

return qwery.value.crdt.dispatch(...args);
return result.value.crdt.dispatch(...args);
},
}) as Dispatch<InferData<I>>;

const reset = new Proxy(noop, {
apply: noop => {
if (!qwery.value?.reset) {
if (!result.value?.reset) {
return noop();
}

return qwery.value.reset();
return result.value.reset();
},
});

if (suspense) {
/// @ts-expect-error: `MaybePromise` throws things off
return qweryPromise.then(result => ({
return pending.then(() => ({
data: computed(
() =>
qwery.value?.crdt.data ??
result?.crdt.data ??
result.value?.crdt?.data ??
(typeof initialValue !== "function"
? initialValue
: null) ??
placeholder ??
null,
placeholder,
),
dispatch,
versions: computed(
() => qwery.value?.crdt.versions ?? result?.crdt.versions,
),
versions: computed(() => result.value?.crdt?.versions),
reset,
}));
}
Expand All @@ -251,13 +280,12 @@ export const useQwery = <
return {
data: computed(
() =>
qwery.value?.crdt?.data ??
result.value?.crdt?.data ??
(typeof initialValue !== "function" ? initialValue : null) ??
placeholder ??
null,
placeholder,
),
dispatch,
versions: computed(() => qwery.value?.crdt?.versions),
versions: computed(() => result.value?.crdt?.versions),
reset,
};
};
Expand Down

0 comments on commit e68ab8d

Please sign in to comment.