Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 53 additions & 11 deletions packages/vue-db/src/useLiveQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,23 @@ import { createLiveQueryCollection } from '@tanstack/db'
import type {
ChangeMessage,
Collection,
CollectionConfigSingleRowOption,
CollectionStatus,
Context,
GetResult,
InferResultType,
InitialQueryBuilder,
LiveQueryCollectionConfig,
NonSingleResult,
QueryBuilder,
SingleResult,
} from '@tanstack/db'
import type { ComputedRef, MaybeRefOrGetter } from 'vue'

/**
* Return type for useLiveQuery hook
* @property state - Reactive Map of query results (key → item)
* @property data - Reactive array of query results in order
* @property data - Reactive array of query results in order, or single result for findOne queries
* @property collection - The underlying query collection instance
* @property status - Current query status
* @property isLoading - True while initial query data is loading
Expand All @@ -33,10 +37,10 @@ import type { ComputedRef, MaybeRefOrGetter } from 'vue'
* @property isError - True when query encountered an error
* @property isCleanedUp - True when query has been cleaned up
*/
export interface UseLiveQueryReturn<T extends object> {
state: ComputedRef<Map<string | number, T>>
data: ComputedRef<Array<T>>
collection: ComputedRef<Collection<T, string | number, {}>>
export interface UseLiveQueryReturn<TContext extends Context> {
state: ComputedRef<Map<string | number, GetResult<TContext>>>
data: ComputedRef<InferResultType<TContext>>
collection: ComputedRef<Collection<GetResult<TContext>, string | number, {}>>
status: ComputedRef<CollectionStatus>
isLoading: ComputedRef<boolean>
isReady: ComputedRef<boolean>
Expand All @@ -61,6 +65,22 @@ export interface UseLiveQueryReturnWithCollection<
isCleanedUp: ComputedRef<boolean>
}

export interface UseLiveQueryReturnWithSingleResultCollection<
T extends object,
TKey extends string | number,
TUtils extends Record<string, any>,
> {
state: ComputedRef<Map<TKey, T>>
data: ComputedRef<T | undefined>
collection: ComputedRef<Collection<T, TKey, TUtils> & SingleResult>
status: ComputedRef<CollectionStatus>
isLoading: ComputedRef<boolean>
isReady: ComputedRef<boolean>
isIdle: ComputedRef<boolean>
isError: ComputedRef<boolean>
isCleanedUp: ComputedRef<boolean>
}

/**
* Create a live query using a query function
* @param queryFn - Query function that defines what data to fetch
Expand Down Expand Up @@ -114,15 +134,15 @@ export interface UseLiveQueryReturnWithCollection<
export function useLiveQuery<TContext extends Context>(
queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>,
deps?: Array<MaybeRefOrGetter<unknown>>,
): UseLiveQueryReturn<GetResult<TContext>>
): UseLiveQueryReturn<TContext>

// Overload 1b: Accept query function that can return undefined/null
export function useLiveQuery<TContext extends Context>(
queryFn: (
q: InitialQueryBuilder,
) => QueryBuilder<TContext> | undefined | null,
deps?: Array<MaybeRefOrGetter<unknown>>,
): UseLiveQueryReturn<GetResult<TContext>>
): UseLiveQueryReturn<TContext>

/**
* Create a live query using configuration object
Expand Down Expand Up @@ -160,7 +180,7 @@ export function useLiveQuery<TContext extends Context>(
export function useLiveQuery<TContext extends Context>(
config: LiveQueryCollectionConfig<TContext>,
deps?: Array<MaybeRefOrGetter<unknown>>,
): UseLiveQueryReturn<GetResult<TContext>>
): UseLiveQueryReturn<TContext>

/**
* Subscribe to an existing query collection (can be reactive)
Expand Down Expand Up @@ -201,15 +221,28 @@ export function useLiveQuery<TContext extends Context>(
* // <Item v-for="item in data" :key="item.id" v-bind="item" />
* // </div>
*/
// Overload 3: Accept pre-created live query collection (can be reactive)
// Overload 3a: Accept pre-created live query collection (can be reactive) - non-single result
export function useLiveQuery<
TResult extends object,
TKey extends string | number,
TUtils extends Record<string, any>,
>(
liveQueryCollection: MaybeRefOrGetter<Collection<TResult, TKey, TUtils>>,
liveQueryCollection: MaybeRefOrGetter<
Collection<TResult, TKey, TUtils> & NonSingleResult
>,
): UseLiveQueryReturnWithCollection<TResult, TKey, TUtils>

// Overload 3b: Accept pre-created live query collection with singleResult: true
export function useLiveQuery<
TResult extends object,
TKey extends string | number,
TUtils extends Record<string, any>,
>(
liveQueryCollection: MaybeRefOrGetter<
Collection<TResult, TKey, TUtils> & SingleResult
>,
): UseLiveQueryReturnWithSingleResultCollection<TResult, TKey, TUtils>

// Implementation
export function useLiveQuery(
configOrQueryOrCollection: any,
Expand Down Expand Up @@ -294,7 +327,16 @@ export function useLiveQuery(
const internalData = reactive<Array<any>>([])

// Computed wrapper for the data to match expected return type
const data = computed(() => internalData)
// Returns single item for singleResult collections, array otherwise
const data = computed(() => {
const currentCollection = collection.value
if (!currentCollection) {
return internalData
}
const config: CollectionConfigSingleRowOption<any, any, any> =
currentCollection.config
return config.singleResult ? internalData[0] : internalData
})

// Track collection status reactively
const status = ref(
Expand Down
8 changes: 4 additions & 4 deletions packages/vue-db/tests/useLiveQuery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ describe(`Query Collections`, () => {
.select(({ issues, persons }) => ({
id: issues.id,
title: issues.title,
name: persons.name,
name: persons?.name,
})),
)

Expand Down Expand Up @@ -590,7 +590,7 @@ describe(`Query Collections`, () => {
.select(({ issues, persons }) => ({
id: issues.id,
title: issues.title,
name: persons.name,
name: persons?.name,
})),
)

Expand Down Expand Up @@ -1162,7 +1162,7 @@ describe(`Query Collections`, () => {
.select(({ issues, persons }) => ({
id: issues.id,
title: issues.title,
name: persons.name,
name: persons?.name,
})),
)

Expand Down Expand Up @@ -1689,7 +1689,7 @@ describe(`Query Collections`, () => {
.select(({ issues, persons }) => ({
id: issues.id,
title: issues.title,
userName: persons.name,
userName: persons?.name,
})),
)

Expand Down
Loading