@tanstack/solid-db
Version:
Solid integration for @tanstack/db
223 lines (222 loc) • 7.69 kB
TypeScript
import { ReactiveMap } from '@solid-primitives/map';
import { Accessor } from 'solid-js';
import { Collection, CollectionStatus, Context, GetResult, InferResultType, InitialQueryBuilder, LiveQueryCollectionConfig, NonSingleResult, QueryBuilder, SingleResult } from '@tanstack/db';
/**
* Create a live query using a query function
* @param queryFn - Query function that defines what data to fetch
* @returns Accessor that returns data with Suspense support, with state and status information as properties
* @example
* // Basic query with object syntax
* const todosQuery = useLiveQuery((q) =>
* q.from({ todos: todosCollection })
* .where(({ todos }) => eq(todos.completed, false))
* .select(({ todos }) => ({ id: todos.id, text: todos.text }))
* )
*
* @example
* // With dependencies that trigger re-execution
* const todosQuery = useLiveQuery(
* (q) => q.from({ todos: todosCollection })
* .where(({ todos }) => gt(todos.priority, minPriority())),
* )
*
* @example
* // Join pattern
* const personIssues = useLiveQuery((q) =>
* q.from({ issues: issueCollection })
* .join({ persons: personCollection }, ({ issues, persons }) =>
* eq(issues.userId, persons.id)
* )
* .select(({ issues, persons }) => ({
* id: issues.id,
* title: issues.title,
* userName: persons.name
* }))
* )
*
* @example
* // Handle loading and error states
* const todosQuery = useLiveQuery((q) =>
* q.from({ todos: todoCollection })
* )
*
* return (
* <Switch>
* <Match when={todosQuery.isLoading}>
* <div>Loading...</div>
* </Match>
* <Match when={todosQuery.isError}>
* <div>Error: {todosQuery.status}</div>
* </Match>
* <Match when={todosQuery.isReady}>
* <For each={todosQuery()}>
* {(todo) => <li key={todo.id}>{todo.text}</li>}
* </For>
* </Match>
* </Switch>
* )
*
* @example
* // Use Suspense boundaries
* const todosQuery = useLiveQuery((q) =>
* q.from({ todos: todoCollection })
* )
*
* return (
* <Suspense fallback={<div>Loading...</div>}>
* <For each={todosQuery()}>
* {(todo) => <li key={todo.id}>{todo.text}</li>}
* </For>
* </Suspense>
* )
*/
export declare function useLiveQuery<TContext extends Context>(queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>): Accessor<InferResultType<TContext>> & {
/**
* @deprecated use function result instead
* query.data -> query()
*/
data: InferResultType<TContext>;
state: ReactiveMap<string | number, GetResult<TContext>>;
collection: Collection<GetResult<TContext>, string | number, {}>;
status: CollectionStatus;
isLoading: boolean;
isReady: boolean;
isIdle: boolean;
isError: boolean;
isCleanedUp: boolean;
};
export declare function useLiveQuery<TContext extends Context>(queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext> | undefined | null): Accessor<InferResultType<TContext>> & {
/**
* @deprecated use function result instead
* query.data -> query()
*/
data: InferResultType<TContext>;
state: ReactiveMap<string | number, GetResult<TContext>>;
collection: Collection<GetResult<TContext>, string | number, {}> | null;
status: CollectionStatus | `disabled`;
isLoading: boolean;
isReady: boolean;
isIdle: boolean;
isError: boolean;
isCleanedUp: boolean;
};
/**
* Create a live query using configuration object
* @param config - Configuration object with query and options
* @returns Accessor that returns data with Suspense support, with state and status information as properties
* @example
* // Basic config object usage
* const todosQuery = useLiveQuery(() => ({
* query: (q) => q.from({ todos: todosCollection }),
* gcTime: 60000
* }))
*
* @example
* // With query builder and options
* const queryBuilder = new Query()
* .from({ persons: collection })
* .where(({ persons }) => gt(persons.age, 30))
* .select(({ persons }) => ({ id: persons.id, name: persons.name }))
*
* const personsQuery = useLiveQuery(() => ({ query: queryBuilder }))
*
* @example
* // Handle all states uniformly
* const itemsQuery = useLiveQuery(() => ({
* query: (q) => q.from({ items: itemCollection })
* }))
*
* return (
* <Switch fallback={<div>{itemsQuery().length} items loaded</div>}>
* <Match when={itemsQuery.isLoading}>
* <div>Loading...</div>
* </Match>
* <Match when={itemsQuery.isError}>
* <div>Something went wrong</div>
* </Match>
* <Match when={!itemsQuery.isReady}>
* <div>Preparing...</div>
* </Match>
* </Switch>
* )
*/
export declare function useLiveQuery<TContext extends Context>(config: Accessor<LiveQueryCollectionConfig<TContext>>): Accessor<InferResultType<TContext>> & {
/**
* @deprecated use function result instead
* query.data -> query()
*/
data: InferResultType<TContext>;
state: ReactiveMap<string | number, GetResult<TContext>>;
collection: Collection<GetResult<TContext>, string | number, {}>;
status: CollectionStatus;
isLoading: boolean;
isReady: boolean;
isIdle: boolean;
isError: boolean;
isCleanedUp: boolean;
};
/**
* Subscribe to an existing live query collection
* @param liveQueryCollection - Pre-created live query collection to subscribe to
* @returns Accessor that returns data with Suspense support, with state and status information as properties
* @example
* // Using pre-created live query collection
* const myLiveQuery = createLiveQueryCollection((q) =>
* q.from({ todos: todosCollection }).where(({ todos }) => eq(todos.active, true))
* )
* const todosQuery = useLiveQuery(() => myLiveQuery)
*
* @example
* // Access collection methods directly
* const existingQuery = useLiveQuery(() => existingCollection)
*
* // Use collection for mutations
* const handleToggle = (id) => {
* existingQuery.collection.update(id, draft => { draft.completed = !draft.completed })
* }
*
* @example
* // Handle states consistently
* const sharedQuery = useLiveQuery(() => sharedCollection)
*
* return (
* <Switch fallback={<div><For each={sharedQuery()}>{(item) => <Item key={item.id} {...item} />}</For></div>}>
* <Match when={sharedQuery.isLoading}>
* <div>Loading...</div>
* </Match>
* <Match when={sharedQuery.isError}>
* <div>Error loading data</div>
* </Match>
* </Switch>
* )
*/
export declare function useLiveQuery<TResult extends object, TKey extends string | number, TUtils extends Record<string, any>>(liveQueryCollection: Accessor<Collection<TResult, TKey, TUtils> & NonSingleResult>): Accessor<Array<TResult>> & {
/**
* @deprecated use function result instead
* query.data -> query()
*/
data: Array<TResult>;
state: ReactiveMap<TKey, TResult>;
collection: Collection<TResult, TKey, TUtils>;
status: CollectionStatus;
isLoading: boolean;
isReady: boolean;
isIdle: boolean;
isError: boolean;
isCleanedUp: boolean;
};
export declare function useLiveQuery<TResult extends object, TKey extends string | number, TUtils extends Record<string, any>>(liveQueryCollection: Accessor<Collection<TResult, TKey, TUtils> & SingleResult>): Accessor<TResult | undefined> & {
/**
* @deprecated use function result instead
* query.data -> query()
*/
data: TResult | undefined;
state: ReactiveMap<TKey, TResult>;
collection: Collection<TResult, TKey, TUtils> & SingleResult;
status: CollectionStatus;
isLoading: boolean;
isReady: boolean;
isIdle: boolean;
isError: boolean;
isCleanedUp: boolean;
};