-
Notifications
You must be signed in to change notification settings - Fork 151
refactor: vue implementation #85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
9dc1e9f
dd7a8da
1ebf8cc
dcfe6ff
8fe7431
eef2668
4947ada
9e5caf6
0d7b1a2
efe9250
492596d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| import { computed, toValue, watch } from "vue" | ||
| import { useStore } from "@tanstack/vue-store" | ||
| import { onWatcherCleanup, shallowRef, toValue, watchEffect } from "vue" | ||
| import { compileQuery, queryBuilder } from "@tanstack/db" | ||
| import { shallow } from "./useStore" | ||
| import type { | ||
| Collection, | ||
| Context, | ||
|
|
@@ -9,12 +9,12 @@ import type { | |
| ResultsFromContext, | ||
| Schema, | ||
| } from "@tanstack/db" | ||
| import type { ComputedRef, MaybeRefOrGetter } from "vue" | ||
| import type { Ref } from "vue" | ||
|
|
||
| export interface UseLiveQueryReturn<T extends object> { | ||
| state: ComputedRef<Map<string, T>> | ||
| data: ComputedRef<Array<T>> | ||
| collection: ComputedRef<Collection<T>> | ||
| state: Readonly<Ref<Map<string, T>>> | ||
| data: Readonly<Ref<Array<T>>> | ||
| collection: Readonly<Ref<Collection<T>>> | ||
| } | ||
|
|
||
| export function useLiveQuery< | ||
|
|
@@ -23,38 +23,60 @@ export function useLiveQuery< | |
| queryFn: ( | ||
| q: InitialQueryBuilder<Context<Schema>> | ||
| ) => QueryBuilder<TResultContext>, | ||
| deps: Array<MaybeRefOrGetter<unknown>> = [] | ||
| deps: () => Array<unknown> = () => [] | ||
| ): UseLiveQueryReturn<ResultsFromContext<TResultContext>> { | ||
| const compiledQuery = computed(() => { | ||
| // Just reference deps to make computed reactive to them | ||
| deps.forEach((dep) => toValue(dep)) | ||
| const results = shallowRef() as Ref< | ||
| ReturnType<typeof compileQuery<TResultContext>>[`results`] | ||
| > | ||
|
|
||
| const state = shallowRef() as Ref< | ||
| Map<string, ResultsFromContext<TResultContext>> | ||
| > | ||
| const data = shallowRef() as Ref<Array<ResultsFromContext<TResultContext>>> | ||
|
|
||
| watchEffect(() => { | ||
| toValue(deps) | ||
|
|
||
| const query = queryFn(queryBuilder()) | ||
| const compiled = compileQuery(query) | ||
| compiled.start() | ||
| return compiled | ||
| }) | ||
|
|
||
| const state = computed(() => { | ||
| return useStore(compiledQuery.value.results.derivedState).value | ||
| }) | ||
| const data = computed(() => { | ||
| return useStore(compiledQuery.value.results.derivedArray).value | ||
| }) | ||
| const resultsRef = compiled.results | ||
| results.value = resultsRef | ||
|
|
||
| const derivedState = resultsRef.derivedState | ||
| const derivedArray = resultsRef.derivedArray | ||
| let stateRef = derivedState.state | ||
| let dataRef = derivedArray.state | ||
| state.value = stateRef | ||
| data.value = dataRef | ||
|
|
||
| watch(compiledQuery, (newQuery, oldQuery, onInvalidate) => { | ||
| if (newQuery.state === `stopped`) { | ||
| newQuery.start() | ||
| } | ||
| const unsubDerivedState = derivedState.subscribe(() => { | ||
| const newValue = derivedState.state | ||
| if (shallow(stateRef, newValue)) return | ||
|
|
||
| stateRef = newValue | ||
| state.value = newValue | ||
| }) | ||
|
|
||
| const unsubDerivedArray = derivedArray.subscribe(() => { | ||
| const newValue = derivedArray.state | ||
| if (shallow(dataRef, newValue)) return | ||
|
|
||
| dataRef = newValue | ||
| data.value = newValue | ||
| }) | ||
|
||
|
|
||
| onInvalidate(() => { | ||
| oldQuery.stop() | ||
| onWatcherCleanup(() => { | ||
| compiled.stop() | ||
| unsubDerivedState() | ||
| unsubDerivedArray() | ||
| }) | ||
| }) | ||
|
|
||
| return { | ||
| state, | ||
| data, | ||
| collection: computed(() => compiledQuery.value.results), | ||
| collection: results, | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| /** | ||
| * @see https://github.com/TanStack/store/blob/cf37b85ddecdcb6f52ad930dcd53e294fb4b03a7/packages/vue-store/src/index.ts#L47 | ||
| */ | ||
|
|
||
| export function shallow<T>(objA: T, objB: T) { | ||
| if (Object.is(objA, objB)) { | ||
| return true | ||
| } | ||
|
|
||
| if ( | ||
| typeof objA !== `object` || | ||
| objA === null || | ||
| typeof objB !== `object` || | ||
| objB === null | ||
| ) { | ||
| return false | ||
| } | ||
|
|
||
| if (objA instanceof Map && objB instanceof Map) { | ||
| if (objA.size !== objB.size) return false | ||
| for (const [k, v] of objA) { | ||
| if (!objB.has(k) || !Object.is(v, objB.get(k))) return false | ||
| } | ||
| return true | ||
| } | ||
|
|
||
| if (objA instanceof Set && objB instanceof Set) { | ||
| if (objA.size !== objB.size) return false | ||
| for (const v of objA) { | ||
| if (!objB.has(v)) return false | ||
| } | ||
| return true | ||
| } | ||
|
|
||
| const keysA = Object.keys(objA) | ||
| if (keysA.length !== Object.keys(objB).length) { | ||
| return false | ||
| } | ||
|
|
||
| for (const keyA of keysA) { | ||
| if ( | ||
| !Object.prototype.hasOwnProperty.call(objB, keyA) || | ||
| !Object.is(objA[keyA as keyof T], objB[keyA as keyof T]) | ||
| ) { | ||
| return false | ||
| } | ||
| } | ||
| return true | ||
| } |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason on why we are not passing the types to the primitive instead?
like
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For
The output will actually be
Ref <T | undefined>because we didn't pass a value