@tanstack/react-db 0.0.17 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLiveQuery.cjs","sources":["../../src/useLiveQuery.ts"],"sourcesContent":["import { useRef, useSyncExternalStore } from \"react\"\nimport { createLiveQueryCollection } from \"@tanstack/db\"\nimport type {\n Collection,\n CollectionStatus,\n Context,\n GetResult,\n InitialQueryBuilder,\n LiveQueryCollectionConfig,\n QueryBuilder,\n} from \"@tanstack/db\"\n\n// Overload 1: Accept just the query function\nexport function useLiveQuery<TContext extends Context>(\n queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>,\n deps?: Array<unknown>\n): {\n state: Map<string | number, GetResult<TContext>>\n data: Array<GetResult<TContext>>\n collection: Collection<GetResult<TContext>, string | number, {}>\n status: CollectionStatus\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n// Overload 2: Accept config object\nexport function useLiveQuery<TContext extends Context>(\n config: LiveQueryCollectionConfig<TContext>,\n deps?: Array<unknown>\n): {\n state: Map<string | number, GetResult<TContext>>\n data: Array<GetResult<TContext>>\n collection: Collection<GetResult<TContext>, string | number, {}>\n status: CollectionStatus\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n// Overload 3: Accept pre-created live query collection\nexport function useLiveQuery<\n TResult extends object,\n TKey extends string | number,\n TUtils extends Record<string, any>,\n>(\n liveQueryCollection: Collection<TResult, TKey, TUtils>\n): {\n state: Map<TKey, TResult>\n data: Array<TResult>\n collection: Collection<TResult, TKey, TUtils>\n status: CollectionStatus\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n// Implementation - use function overloads to infer the actual collection type\nexport function useLiveQuery(\n configOrQueryOrCollection: any,\n deps: Array<unknown> = []\n) {\n // Check if it's already a collection by checking for specific collection methods\n const isCollection =\n configOrQueryOrCollection &&\n typeof configOrQueryOrCollection === `object` &&\n typeof configOrQueryOrCollection.subscribeChanges === `function` &&\n typeof configOrQueryOrCollection.startSyncImmediate === `function` &&\n typeof configOrQueryOrCollection.id === `string`\n\n // Use refs to cache collection and track dependencies\n const collectionRef = useRef<any>(null)\n const depsRef = useRef<Array<unknown> | null>(null)\n const configRef = useRef<any>(null)\n\n // Check if we need to create/recreate the collection\n const needsNewCollection =\n !collectionRef.current ||\n (isCollection && configRef.current !== configOrQueryOrCollection) ||\n (!isCollection &&\n (depsRef.current === null ||\n depsRef.current.length !== deps.length ||\n depsRef.current.some((dep, i) => dep !== deps[i])))\n\n if (needsNewCollection) {\n if (isCollection) {\n // It's already a collection, ensure sync is started for React hooks\n configOrQueryOrCollection.startSyncImmediate()\n collectionRef.current = configOrQueryOrCollection\n configRef.current = configOrQueryOrCollection\n } else {\n // Original logic for creating collections\n // Ensure we always start sync for React hooks\n if (typeof configOrQueryOrCollection === `function`) {\n collectionRef.current = createLiveQueryCollection({\n query: configOrQueryOrCollection,\n startSync: true,\n gcTime: 0, // Live queries created by useLiveQuery are cleaned up immediately\n })\n } else {\n collectionRef.current = createLiveQueryCollection({\n startSync: true,\n gcTime: 0, // Live queries created by useLiveQuery are cleaned up immediately\n ...configOrQueryOrCollection,\n })\n }\n depsRef.current = [...deps]\n }\n }\n\n // Use refs to track version and memoized snapshot\n const versionRef = useRef(0)\n const snapshotRef = useRef<{\n state: Map<any, any>\n data: Array<any>\n collection: Collection<any, any, any>\n _version: number\n } | null>(null)\n\n // Reset refs when collection changes\n if (needsNewCollection) {\n versionRef.current = 0\n snapshotRef.current = null\n }\n\n // Create stable subscribe function using ref\n const subscribeRef = useRef<\n ((onStoreChange: () => void) => () => void) | null\n >(null)\n if (!subscribeRef.current || needsNewCollection) {\n subscribeRef.current = (onStoreChange: () => void) => {\n const unsubscribe = collectionRef.current!.subscribeChanges(() => {\n versionRef.current += 1\n onStoreChange()\n })\n return () => {\n unsubscribe()\n }\n }\n }\n\n // Create stable getSnapshot function using ref\n const getSnapshotRef = useRef<\n | (() => {\n state: Map<any, any>\n data: Array<any>\n collection: Collection<any, any, any>\n })\n | null\n >(null)\n if (!getSnapshotRef.current || needsNewCollection) {\n getSnapshotRef.current = () => {\n const currentVersion = versionRef.current\n const currentCollection = collectionRef.current!\n\n // If we don't have a snapshot or the version changed, create a new one\n if (\n !snapshotRef.current ||\n snapshotRef.current._version !== currentVersion\n ) {\n snapshotRef.current = {\n get state() {\n return new Map(currentCollection.entries())\n },\n get data() {\n return Array.from(currentCollection.values())\n },\n collection: currentCollection,\n _version: currentVersion,\n }\n }\n\n return snapshotRef.current\n }\n }\n\n // Use useSyncExternalStore to subscribe to collection changes\n const snapshot = useSyncExternalStore(\n subscribeRef.current,\n getSnapshotRef.current\n )\n\n return {\n state: snapshot.state,\n data: snapshot.data,\n collection: snapshot.collection,\n status: snapshot.collection.status,\n isLoading:\n snapshot.collection.status === `loading` ||\n snapshot.collection.status === `initialCommit`,\n isReady: snapshot.collection.status === `ready`,\n isIdle: snapshot.collection.status === `idle`,\n isError: snapshot.collection.status === `error`,\n isCleanedUp: snapshot.collection.status === `cleaned-up`,\n }\n}\n"],"names":["useRef","createLiveQueryCollection","useSyncExternalStore"],"mappings":";;;;AAgEO,SAAS,aACd,2BACA,OAAuB,IACvB;AAEA,QAAM,eACJ,6BACA,OAAO,8BAA8B,YACrC,OAAO,0BAA0B,qBAAqB,cACtD,OAAO,0BAA0B,uBAAuB,cACxD,OAAO,0BAA0B,OAAO;AAGpC,QAAA,gBAAgBA,aAAY,IAAI;AAChC,QAAA,UAAUA,aAA8B,IAAI;AAC5C,QAAA,YAAYA,aAAY,IAAI;AAG5B,QAAA,qBACJ,CAAC,cAAc,WACd,gBAAgB,UAAU,YAAY,6BACtC,CAAC,iBACC,QAAQ,YAAY,QACnB,QAAQ,QAAQ,WAAW,KAAK,UAChC,QAAQ,QAAQ,KAAK,CAAC,KAAK,MAAM,QAAQ,KAAK,CAAC,CAAC;AAEtD,MAAI,oBAAoB;AACtB,QAAI,cAAc;AAEhB,gCAA0B,mBAAmB;AAC7C,oBAAc,UAAU;AACxB,gBAAU,UAAU;AAAA,IAAA,OACf;AAGD,UAAA,OAAO,8BAA8B,YAAY;AACnD,sBAAc,UAAUC,6BAA0B;AAAA,UAChD,OAAO;AAAA,UACP,WAAW;AAAA,UACX,QAAQ;AAAA;AAAA,QAAA,CACT;AAAA,MAAA,OACI;AACL,sBAAc,UAAUA,6BAA0B;AAAA,UAChD,WAAW;AAAA,UACX,QAAQ;AAAA;AAAA,UACR,GAAG;AAAA,QAAA,CACJ;AAAA,MAAA;AAEK,cAAA,UAAU,CAAC,GAAG,IAAI;AAAA,IAAA;AAAA,EAC5B;AAII,QAAA,aAAaD,aAAO,CAAC;AACrB,QAAA,cAAcA,aAKV,IAAI;AAGd,MAAI,oBAAoB;AACtB,eAAW,UAAU;AACrB,gBAAY,UAAU;AAAA,EAAA;AAIlB,QAAA,eAAeA,aAEnB,IAAI;AACF,MAAA,CAAC,aAAa,WAAW,oBAAoB;AAClC,iBAAA,UAAU,CAAC,kBAA8B;AACpD,YAAM,cAAc,cAAc,QAAS,iBAAiB,MAAM;AAChE,mBAAW,WAAW;AACR,sBAAA;AAAA,MAAA,CACf;AACD,aAAO,MAAM;AACC,oBAAA;AAAA,MACd;AAAA,IACF;AAAA,EAAA;AAII,QAAA,iBAAiBA,aAOrB,IAAI;AACF,MAAA,CAAC,eAAe,WAAW,oBAAoB;AACjD,mBAAe,UAAU,MAAM;AAC7B,YAAM,iBAAiB,WAAW;AAClC,YAAM,oBAAoB,cAAc;AAGxC,UACE,CAAC,YAAY,WACb,YAAY,QAAQ,aAAa,gBACjC;AACA,oBAAY,UAAU;AAAA,UACpB,IAAI,QAAQ;AACV,mBAAO,IAAI,IAAI,kBAAkB,SAAS;AAAA,UAC5C;AAAA,UACA,IAAI,OAAO;AACT,mBAAO,MAAM,KAAK,kBAAkB,OAAA,CAAQ;AAAA,UAC9C;AAAA,UACA,YAAY;AAAA,UACZ,UAAU;AAAA,QACZ;AAAA,MAAA;AAGF,aAAO,YAAY;AAAA,IACrB;AAAA,EAAA;AAIF,QAAM,WAAWE,MAAA;AAAA,IACf,aAAa;AAAA,IACb,eAAe;AAAA,EACjB;AAEO,SAAA;AAAA,IACL,OAAO,SAAS;AAAA,IAChB,MAAM,SAAS;AAAA,IACf,YAAY,SAAS;AAAA,IACrB,QAAQ,SAAS,WAAW;AAAA,IAC5B,WACE,SAAS,WAAW,WAAW,aAC/B,SAAS,WAAW,WAAW;AAAA,IACjC,SAAS,SAAS,WAAW,WAAW;AAAA,IACxC,QAAQ,SAAS,WAAW,WAAW;AAAA,IACvC,SAAS,SAAS,WAAW,WAAW;AAAA,IACxC,aAAa,SAAS,WAAW,WAAW;AAAA,EAC9C;AACF;;"}
|
|
1
|
+
{"version":3,"file":"useLiveQuery.cjs","sources":["../../src/useLiveQuery.ts"],"sourcesContent":["import { useRef, useSyncExternalStore } from \"react\"\nimport { createLiveQueryCollection } from \"@tanstack/db\"\nimport type {\n Collection,\n CollectionStatus,\n Context,\n GetResult,\n InitialQueryBuilder,\n LiveQueryCollectionConfig,\n QueryBuilder,\n} from \"@tanstack/db\"\n\n/**\n * Create a live query using a query function\n * @param queryFn - Query function that defines what data to fetch\n * @param deps - Array of dependencies that trigger query re-execution when changed\n * @returns Object with reactive data, state, and status information\n * @example\n * // Basic query with object syntax\n * const { data, isLoading } = useLiveQuery((q) =>\n * q.from({ todos: todosCollection })\n * .where(({ todos }) => eq(todos.completed, false))\n * .select(({ todos }) => ({ id: todos.id, text: todos.text }))\n * )\n *\n * @example\n * // With dependencies that trigger re-execution\n * const { data, state } = useLiveQuery(\n * (q) => q.from({ todos: todosCollection })\n * .where(({ todos }) => gt(todos.priority, minPriority)),\n * [minPriority] // Re-run when minPriority changes\n * )\n *\n * @example\n * // Join pattern\n * const { data } = useLiveQuery((q) =>\n * q.from({ issues: issueCollection })\n * .join({ persons: personCollection }, ({ issues, persons }) =>\n * eq(issues.userId, persons.id)\n * )\n * .select(({ issues, persons }) => ({\n * id: issues.id,\n * title: issues.title,\n * userName: persons.name\n * }))\n * )\n *\n * @example\n * // Handle loading and error states\n * const { data, isLoading, isError, status } = useLiveQuery((q) =>\n * q.from({ todos: todoCollection })\n * )\n *\n * if (isLoading) return <div>Loading...</div>\n * if (isError) return <div>Error: {status}</div>\n *\n * return (\n * <ul>\n * {data.map(todo => <li key={todo.id}>{todo.text}</li>)}\n * </ul>\n * )\n */\n// Overload 1: Accept just the query function\nexport function useLiveQuery<TContext extends Context>(\n queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>,\n deps?: Array<unknown>\n): {\n state: Map<string | number, GetResult<TContext>>\n data: Array<GetResult<TContext>>\n collection: Collection<GetResult<TContext>, string | number, {}>\n status: CollectionStatus\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n/**\n * Create a live query using configuration object\n * @param config - Configuration object with query and options\n * @param deps - Array of dependencies that trigger query re-execution when changed\n * @returns Object with reactive data, state, and status information\n * @example\n * // Basic config object usage\n * const { data, status } = useLiveQuery({\n * query: (q) => q.from({ todos: todosCollection }),\n * gcTime: 60000\n * })\n *\n * @example\n * // With query builder and options\n * const queryBuilder = new Query()\n * .from({ persons: collection })\n * .where(({ persons }) => gt(persons.age, 30))\n * .select(({ persons }) => ({ id: persons.id, name: persons.name }))\n *\n * const { data, isReady } = useLiveQuery({ query: queryBuilder })\n *\n * @example\n * // Handle all states uniformly\n * const { data, isLoading, isReady, isError } = useLiveQuery({\n * query: (q) => q.from({ items: itemCollection })\n * })\n *\n * if (isLoading) return <div>Loading...</div>\n * if (isError) return <div>Something went wrong</div>\n * if (!isReady) return <div>Preparing...</div>\n *\n * return <div>{data.length} items loaded</div>\n */\n// Overload 2: Accept config object\nexport function useLiveQuery<TContext extends Context>(\n config: LiveQueryCollectionConfig<TContext>,\n deps?: Array<unknown>\n): {\n state: Map<string | number, GetResult<TContext>>\n data: Array<GetResult<TContext>>\n collection: Collection<GetResult<TContext>, string | number, {}>\n status: CollectionStatus\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n/**\n * Subscribe to an existing live query collection\n * @param liveQueryCollection - Pre-created live query collection to subscribe to\n * @returns Object with reactive data, state, and status information\n * @example\n * // Using pre-created live query collection\n * const myLiveQuery = createLiveQueryCollection((q) =>\n * q.from({ todos: todosCollection }).where(({ todos }) => eq(todos.active, true))\n * )\n * const { data, collection } = useLiveQuery(myLiveQuery)\n *\n * @example\n * // Access collection methods directly\n * const { data, collection, isReady } = useLiveQuery(existingCollection)\n *\n * // Use collection for mutations\n * const handleToggle = (id) => {\n * collection.update(id, draft => { draft.completed = !draft.completed })\n * }\n *\n * @example\n * // Handle states consistently\n * const { data, isLoading, isError } = useLiveQuery(sharedCollection)\n *\n * if (isLoading) return <div>Loading...</div>\n * if (isError) return <div>Error loading data</div>\n *\n * return <div>{data.map(item => <Item key={item.id} {...item} />)}</div>\n */\n// Overload 3: Accept pre-created live query collection\nexport function useLiveQuery<\n TResult extends object,\n TKey extends string | number,\n TUtils extends Record<string, any>,\n>(\n liveQueryCollection: Collection<TResult, TKey, TUtils>\n): {\n state: Map<TKey, TResult>\n data: Array<TResult>\n collection: Collection<TResult, TKey, TUtils>\n status: CollectionStatus\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n// Implementation - use function overloads to infer the actual collection type\nexport function useLiveQuery(\n configOrQueryOrCollection: any,\n deps: Array<unknown> = []\n) {\n // Check if it's already a collection by checking for specific collection methods\n const isCollection =\n configOrQueryOrCollection &&\n typeof configOrQueryOrCollection === `object` &&\n typeof configOrQueryOrCollection.subscribeChanges === `function` &&\n typeof configOrQueryOrCollection.startSyncImmediate === `function` &&\n typeof configOrQueryOrCollection.id === `string`\n\n // Use refs to cache collection and track dependencies\n const collectionRef = useRef<any>(null)\n const depsRef = useRef<Array<unknown> | null>(null)\n const configRef = useRef<any>(null)\n\n // Check if we need to create/recreate the collection\n const needsNewCollection =\n !collectionRef.current ||\n (isCollection && configRef.current !== configOrQueryOrCollection) ||\n (!isCollection &&\n (depsRef.current === null ||\n depsRef.current.length !== deps.length ||\n depsRef.current.some((dep, i) => dep !== deps[i])))\n\n if (needsNewCollection) {\n if (isCollection) {\n // It's already a collection, ensure sync is started for React hooks\n configOrQueryOrCollection.startSyncImmediate()\n collectionRef.current = configOrQueryOrCollection\n configRef.current = configOrQueryOrCollection\n } else {\n // Original logic for creating collections\n // Ensure we always start sync for React hooks\n if (typeof configOrQueryOrCollection === `function`) {\n collectionRef.current = createLiveQueryCollection({\n query: configOrQueryOrCollection,\n startSync: true,\n gcTime: 0, // Live queries created by useLiveQuery are cleaned up immediately\n })\n } else {\n collectionRef.current = createLiveQueryCollection({\n startSync: true,\n gcTime: 0, // Live queries created by useLiveQuery are cleaned up immediately\n ...configOrQueryOrCollection,\n })\n }\n depsRef.current = [...deps]\n }\n }\n\n // Use refs to track version and memoized snapshot\n const versionRef = useRef(0)\n const snapshotRef = useRef<{\n state: Map<any, any>\n data: Array<any>\n collection: Collection<any, any, any>\n _version: number\n } | null>(null)\n\n // Reset refs when collection changes\n if (needsNewCollection) {\n versionRef.current = 0\n snapshotRef.current = null\n }\n\n // Create stable subscribe function using ref\n const subscribeRef = useRef<\n ((onStoreChange: () => void) => () => void) | null\n >(null)\n if (!subscribeRef.current || needsNewCollection) {\n subscribeRef.current = (onStoreChange: () => void) => {\n const unsubscribe = collectionRef.current!.subscribeChanges(() => {\n versionRef.current += 1\n onStoreChange()\n })\n return () => {\n unsubscribe()\n }\n }\n }\n\n // Create stable getSnapshot function using ref\n const getSnapshotRef = useRef<\n | (() => {\n state: Map<any, any>\n data: Array<any>\n collection: Collection<any, any, any>\n })\n | null\n >(null)\n if (!getSnapshotRef.current || needsNewCollection) {\n getSnapshotRef.current = () => {\n const currentVersion = versionRef.current\n const currentCollection = collectionRef.current!\n\n // If we don't have a snapshot or the version changed, create a new one\n if (\n !snapshotRef.current ||\n snapshotRef.current._version !== currentVersion\n ) {\n snapshotRef.current = {\n get state() {\n return new Map(currentCollection.entries())\n },\n get data() {\n return Array.from(currentCollection.values())\n },\n collection: currentCollection,\n _version: currentVersion,\n }\n }\n\n return snapshotRef.current\n }\n }\n\n // Use useSyncExternalStore to subscribe to collection changes\n const snapshot = useSyncExternalStore(\n subscribeRef.current,\n getSnapshotRef.current\n )\n\n return {\n state: snapshot.state,\n data: snapshot.data,\n collection: snapshot.collection,\n status: snapshot.collection.status,\n isLoading:\n snapshot.collection.status === `loading` ||\n snapshot.collection.status === `initialCommit`,\n isReady: snapshot.collection.status === `ready`,\n isIdle: snapshot.collection.status === `idle`,\n isError: snapshot.collection.status === `error`,\n isCleanedUp: snapshot.collection.status === `cleaned-up`,\n }\n}\n"],"names":["useRef","createLiveQueryCollection","useSyncExternalStore"],"mappings":";;;;AAgLO,SAAS,aACd,2BACA,OAAuB,IACvB;AAEA,QAAM,eACJ,6BACA,OAAO,8BAA8B,YACrC,OAAO,0BAA0B,qBAAqB,cACtD,OAAO,0BAA0B,uBAAuB,cACxD,OAAO,0BAA0B,OAAO;AAG1C,QAAM,gBAAgBA,MAAAA,OAAY,IAAI;AACtC,QAAM,UAAUA,MAAAA,OAA8B,IAAI;AAClD,QAAM,YAAYA,MAAAA,OAAY,IAAI;AAGlC,QAAM,qBACJ,CAAC,cAAc,WACd,gBAAgB,UAAU,YAAY,6BACtC,CAAC,iBACC,QAAQ,YAAY,QACnB,QAAQ,QAAQ,WAAW,KAAK,UAChC,QAAQ,QAAQ,KAAK,CAAC,KAAK,MAAM,QAAQ,KAAK,CAAC,CAAC;AAEtD,MAAI,oBAAoB;AACtB,QAAI,cAAc;AAEhB,gCAA0B,mBAAA;AAC1B,oBAAc,UAAU;AACxB,gBAAU,UAAU;AAAA,IACtB,OAAO;AAGL,UAAI,OAAO,8BAA8B,YAAY;AACnD,sBAAc,UAAUC,6BAA0B;AAAA,UAChD,OAAO;AAAA,UACP,WAAW;AAAA,UACX,QAAQ;AAAA;AAAA,QAAA,CACT;AAAA,MACH,OAAO;AACL,sBAAc,UAAUA,6BAA0B;AAAA,UAChD,WAAW;AAAA,UACX,QAAQ;AAAA;AAAA,UACR,GAAG;AAAA,QAAA,CACJ;AAAA,MACH;AACA,cAAQ,UAAU,CAAC,GAAG,IAAI;AAAA,IAC5B;AAAA,EACF;AAGA,QAAM,aAAaD,MAAAA,OAAO,CAAC;AAC3B,QAAM,cAAcA,MAAAA,OAKV,IAAI;AAGd,MAAI,oBAAoB;AACtB,eAAW,UAAU;AACrB,gBAAY,UAAU;AAAA,EACxB;AAGA,QAAM,eAAeA,MAAAA,OAEnB,IAAI;AACN,MAAI,CAAC,aAAa,WAAW,oBAAoB;AAC/C,iBAAa,UAAU,CAAC,kBAA8B;AACpD,YAAM,cAAc,cAAc,QAAS,iBAAiB,MAAM;AAChE,mBAAW,WAAW;AACtB,sBAAA;AAAA,MACF,CAAC;AACD,aAAO,MAAM;AACX,oBAAA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,QAAM,iBAAiBA,MAAAA,OAOrB,IAAI;AACN,MAAI,CAAC,eAAe,WAAW,oBAAoB;AACjD,mBAAe,UAAU,MAAM;AAC7B,YAAM,iBAAiB,WAAW;AAClC,YAAM,oBAAoB,cAAc;AAGxC,UACE,CAAC,YAAY,WACb,YAAY,QAAQ,aAAa,gBACjC;AACA,oBAAY,UAAU;AAAA,UACpB,IAAI,QAAQ;AACV,mBAAO,IAAI,IAAI,kBAAkB,SAAS;AAAA,UAC5C;AAAA,UACA,IAAI,OAAO;AACT,mBAAO,MAAM,KAAK,kBAAkB,OAAA,CAAQ;AAAA,UAC9C;AAAA,UACA,YAAY;AAAA,UACZ,UAAU;AAAA,QAAA;AAAA,MAEd;AAEA,aAAO,YAAY;AAAA,IACrB;AAAA,EACF;AAGA,QAAM,WAAWE,MAAAA;AAAAA,IACf,aAAa;AAAA,IACb,eAAe;AAAA,EAAA;AAGjB,SAAO;AAAA,IACL,OAAO,SAAS;AAAA,IAChB,MAAM,SAAS;AAAA,IACf,YAAY,SAAS;AAAA,IACrB,QAAQ,SAAS,WAAW;AAAA,IAC5B,WACE,SAAS,WAAW,WAAW,aAC/B,SAAS,WAAW,WAAW;AAAA,IACjC,SAAS,SAAS,WAAW,WAAW;AAAA,IACxC,QAAQ,SAAS,WAAW,WAAW;AAAA,IACvC,SAAS,SAAS,WAAW,WAAW;AAAA,IACxC,aAAa,SAAS,WAAW,WAAW;AAAA,EAAA;AAEhD;;"}
|
|
@@ -1,4 +1,54 @@
|
|
|
1
1
|
import { Collection, CollectionStatus, Context, GetResult, InitialQueryBuilder, LiveQueryCollectionConfig, QueryBuilder } from '@tanstack/db';
|
|
2
|
+
/**
|
|
3
|
+
* Create a live query using a query function
|
|
4
|
+
* @param queryFn - Query function that defines what data to fetch
|
|
5
|
+
* @param deps - Array of dependencies that trigger query re-execution when changed
|
|
6
|
+
* @returns Object with reactive data, state, and status information
|
|
7
|
+
* @example
|
|
8
|
+
* // Basic query with object syntax
|
|
9
|
+
* const { data, isLoading } = useLiveQuery((q) =>
|
|
10
|
+
* q.from({ todos: todosCollection })
|
|
11
|
+
* .where(({ todos }) => eq(todos.completed, false))
|
|
12
|
+
* .select(({ todos }) => ({ id: todos.id, text: todos.text }))
|
|
13
|
+
* )
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* // With dependencies that trigger re-execution
|
|
17
|
+
* const { data, state } = useLiveQuery(
|
|
18
|
+
* (q) => q.from({ todos: todosCollection })
|
|
19
|
+
* .where(({ todos }) => gt(todos.priority, minPriority)),
|
|
20
|
+
* [minPriority] // Re-run when minPriority changes
|
|
21
|
+
* )
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // Join pattern
|
|
25
|
+
* const { data } = useLiveQuery((q) =>
|
|
26
|
+
* q.from({ issues: issueCollection })
|
|
27
|
+
* .join({ persons: personCollection }, ({ issues, persons }) =>
|
|
28
|
+
* eq(issues.userId, persons.id)
|
|
29
|
+
* )
|
|
30
|
+
* .select(({ issues, persons }) => ({
|
|
31
|
+
* id: issues.id,
|
|
32
|
+
* title: issues.title,
|
|
33
|
+
* userName: persons.name
|
|
34
|
+
* }))
|
|
35
|
+
* )
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* // Handle loading and error states
|
|
39
|
+
* const { data, isLoading, isError, status } = useLiveQuery((q) =>
|
|
40
|
+
* q.from({ todos: todoCollection })
|
|
41
|
+
* )
|
|
42
|
+
*
|
|
43
|
+
* if (isLoading) return <div>Loading...</div>
|
|
44
|
+
* if (isError) return <div>Error: {status}</div>
|
|
45
|
+
*
|
|
46
|
+
* return (
|
|
47
|
+
* <ul>
|
|
48
|
+
* {data.map(todo => <li key={todo.id}>{todo.text}</li>)}
|
|
49
|
+
* </ul>
|
|
50
|
+
* )
|
|
51
|
+
*/
|
|
2
52
|
export declare function useLiveQuery<TContext extends Context>(queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>, deps?: Array<unknown>): {
|
|
3
53
|
state: Map<string | number, GetResult<TContext>>;
|
|
4
54
|
data: Array<GetResult<TContext>>;
|
|
@@ -10,6 +60,39 @@ export declare function useLiveQuery<TContext extends Context>(queryFn: (q: Init
|
|
|
10
60
|
isError: boolean;
|
|
11
61
|
isCleanedUp: boolean;
|
|
12
62
|
};
|
|
63
|
+
/**
|
|
64
|
+
* Create a live query using configuration object
|
|
65
|
+
* @param config - Configuration object with query and options
|
|
66
|
+
* @param deps - Array of dependencies that trigger query re-execution when changed
|
|
67
|
+
* @returns Object with reactive data, state, and status information
|
|
68
|
+
* @example
|
|
69
|
+
* // Basic config object usage
|
|
70
|
+
* const { data, status } = useLiveQuery({
|
|
71
|
+
* query: (q) => q.from({ todos: todosCollection }),
|
|
72
|
+
* gcTime: 60000
|
|
73
|
+
* })
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* // With query builder and options
|
|
77
|
+
* const queryBuilder = new Query()
|
|
78
|
+
* .from({ persons: collection })
|
|
79
|
+
* .where(({ persons }) => gt(persons.age, 30))
|
|
80
|
+
* .select(({ persons }) => ({ id: persons.id, name: persons.name }))
|
|
81
|
+
*
|
|
82
|
+
* const { data, isReady } = useLiveQuery({ query: queryBuilder })
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* // Handle all states uniformly
|
|
86
|
+
* const { data, isLoading, isReady, isError } = useLiveQuery({
|
|
87
|
+
* query: (q) => q.from({ items: itemCollection })
|
|
88
|
+
* })
|
|
89
|
+
*
|
|
90
|
+
* if (isLoading) return <div>Loading...</div>
|
|
91
|
+
* if (isError) return <div>Something went wrong</div>
|
|
92
|
+
* if (!isReady) return <div>Preparing...</div>
|
|
93
|
+
*
|
|
94
|
+
* return <div>{data.length} items loaded</div>
|
|
95
|
+
*/
|
|
13
96
|
export declare function useLiveQuery<TContext extends Context>(config: LiveQueryCollectionConfig<TContext>, deps?: Array<unknown>): {
|
|
14
97
|
state: Map<string | number, GetResult<TContext>>;
|
|
15
98
|
data: Array<GetResult<TContext>>;
|
|
@@ -21,6 +104,35 @@ export declare function useLiveQuery<TContext extends Context>(config: LiveQuery
|
|
|
21
104
|
isError: boolean;
|
|
22
105
|
isCleanedUp: boolean;
|
|
23
106
|
};
|
|
107
|
+
/**
|
|
108
|
+
* Subscribe to an existing live query collection
|
|
109
|
+
* @param liveQueryCollection - Pre-created live query collection to subscribe to
|
|
110
|
+
* @returns Object with reactive data, state, and status information
|
|
111
|
+
* @example
|
|
112
|
+
* // Using pre-created live query collection
|
|
113
|
+
* const myLiveQuery = createLiveQueryCollection((q) =>
|
|
114
|
+
* q.from({ todos: todosCollection }).where(({ todos }) => eq(todos.active, true))
|
|
115
|
+
* )
|
|
116
|
+
* const { data, collection } = useLiveQuery(myLiveQuery)
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* // Access collection methods directly
|
|
120
|
+
* const { data, collection, isReady } = useLiveQuery(existingCollection)
|
|
121
|
+
*
|
|
122
|
+
* // Use collection for mutations
|
|
123
|
+
* const handleToggle = (id) => {
|
|
124
|
+
* collection.update(id, draft => { draft.completed = !draft.completed })
|
|
125
|
+
* }
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* // Handle states consistently
|
|
129
|
+
* const { data, isLoading, isError } = useLiveQuery(sharedCollection)
|
|
130
|
+
*
|
|
131
|
+
* if (isLoading) return <div>Loading...</div>
|
|
132
|
+
* if (isError) return <div>Error loading data</div>
|
|
133
|
+
*
|
|
134
|
+
* return <div>{data.map(item => <Item key={item.id} {...item} />)}</div>
|
|
135
|
+
*/
|
|
24
136
|
export declare function useLiveQuery<TResult extends object, TKey extends string | number, TUtils extends Record<string, any>>(liveQueryCollection: Collection<TResult, TKey, TUtils>): {
|
|
25
137
|
state: Map<TKey, TResult>;
|
|
26
138
|
data: Array<TResult>;
|
|
@@ -1,4 +1,54 @@
|
|
|
1
1
|
import { Collection, CollectionStatus, Context, GetResult, InitialQueryBuilder, LiveQueryCollectionConfig, QueryBuilder } from '@tanstack/db';
|
|
2
|
+
/**
|
|
3
|
+
* Create a live query using a query function
|
|
4
|
+
* @param queryFn - Query function that defines what data to fetch
|
|
5
|
+
* @param deps - Array of dependencies that trigger query re-execution when changed
|
|
6
|
+
* @returns Object with reactive data, state, and status information
|
|
7
|
+
* @example
|
|
8
|
+
* // Basic query with object syntax
|
|
9
|
+
* const { data, isLoading } = useLiveQuery((q) =>
|
|
10
|
+
* q.from({ todos: todosCollection })
|
|
11
|
+
* .where(({ todos }) => eq(todos.completed, false))
|
|
12
|
+
* .select(({ todos }) => ({ id: todos.id, text: todos.text }))
|
|
13
|
+
* )
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* // With dependencies that trigger re-execution
|
|
17
|
+
* const { data, state } = useLiveQuery(
|
|
18
|
+
* (q) => q.from({ todos: todosCollection })
|
|
19
|
+
* .where(({ todos }) => gt(todos.priority, minPriority)),
|
|
20
|
+
* [minPriority] // Re-run when minPriority changes
|
|
21
|
+
* )
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // Join pattern
|
|
25
|
+
* const { data } = useLiveQuery((q) =>
|
|
26
|
+
* q.from({ issues: issueCollection })
|
|
27
|
+
* .join({ persons: personCollection }, ({ issues, persons }) =>
|
|
28
|
+
* eq(issues.userId, persons.id)
|
|
29
|
+
* )
|
|
30
|
+
* .select(({ issues, persons }) => ({
|
|
31
|
+
* id: issues.id,
|
|
32
|
+
* title: issues.title,
|
|
33
|
+
* userName: persons.name
|
|
34
|
+
* }))
|
|
35
|
+
* )
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* // Handle loading and error states
|
|
39
|
+
* const { data, isLoading, isError, status } = useLiveQuery((q) =>
|
|
40
|
+
* q.from({ todos: todoCollection })
|
|
41
|
+
* )
|
|
42
|
+
*
|
|
43
|
+
* if (isLoading) return <div>Loading...</div>
|
|
44
|
+
* if (isError) return <div>Error: {status}</div>
|
|
45
|
+
*
|
|
46
|
+
* return (
|
|
47
|
+
* <ul>
|
|
48
|
+
* {data.map(todo => <li key={todo.id}>{todo.text}</li>)}
|
|
49
|
+
* </ul>
|
|
50
|
+
* )
|
|
51
|
+
*/
|
|
2
52
|
export declare function useLiveQuery<TContext extends Context>(queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>, deps?: Array<unknown>): {
|
|
3
53
|
state: Map<string | number, GetResult<TContext>>;
|
|
4
54
|
data: Array<GetResult<TContext>>;
|
|
@@ -10,6 +60,39 @@ export declare function useLiveQuery<TContext extends Context>(queryFn: (q: Init
|
|
|
10
60
|
isError: boolean;
|
|
11
61
|
isCleanedUp: boolean;
|
|
12
62
|
};
|
|
63
|
+
/**
|
|
64
|
+
* Create a live query using configuration object
|
|
65
|
+
* @param config - Configuration object with query and options
|
|
66
|
+
* @param deps - Array of dependencies that trigger query re-execution when changed
|
|
67
|
+
* @returns Object with reactive data, state, and status information
|
|
68
|
+
* @example
|
|
69
|
+
* // Basic config object usage
|
|
70
|
+
* const { data, status } = useLiveQuery({
|
|
71
|
+
* query: (q) => q.from({ todos: todosCollection }),
|
|
72
|
+
* gcTime: 60000
|
|
73
|
+
* })
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* // With query builder and options
|
|
77
|
+
* const queryBuilder = new Query()
|
|
78
|
+
* .from({ persons: collection })
|
|
79
|
+
* .where(({ persons }) => gt(persons.age, 30))
|
|
80
|
+
* .select(({ persons }) => ({ id: persons.id, name: persons.name }))
|
|
81
|
+
*
|
|
82
|
+
* const { data, isReady } = useLiveQuery({ query: queryBuilder })
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* // Handle all states uniformly
|
|
86
|
+
* const { data, isLoading, isReady, isError } = useLiveQuery({
|
|
87
|
+
* query: (q) => q.from({ items: itemCollection })
|
|
88
|
+
* })
|
|
89
|
+
*
|
|
90
|
+
* if (isLoading) return <div>Loading...</div>
|
|
91
|
+
* if (isError) return <div>Something went wrong</div>
|
|
92
|
+
* if (!isReady) return <div>Preparing...</div>
|
|
93
|
+
*
|
|
94
|
+
* return <div>{data.length} items loaded</div>
|
|
95
|
+
*/
|
|
13
96
|
export declare function useLiveQuery<TContext extends Context>(config: LiveQueryCollectionConfig<TContext>, deps?: Array<unknown>): {
|
|
14
97
|
state: Map<string | number, GetResult<TContext>>;
|
|
15
98
|
data: Array<GetResult<TContext>>;
|
|
@@ -21,6 +104,35 @@ export declare function useLiveQuery<TContext extends Context>(config: LiveQuery
|
|
|
21
104
|
isError: boolean;
|
|
22
105
|
isCleanedUp: boolean;
|
|
23
106
|
};
|
|
107
|
+
/**
|
|
108
|
+
* Subscribe to an existing live query collection
|
|
109
|
+
* @param liveQueryCollection - Pre-created live query collection to subscribe to
|
|
110
|
+
* @returns Object with reactive data, state, and status information
|
|
111
|
+
* @example
|
|
112
|
+
* // Using pre-created live query collection
|
|
113
|
+
* const myLiveQuery = createLiveQueryCollection((q) =>
|
|
114
|
+
* q.from({ todos: todosCollection }).where(({ todos }) => eq(todos.active, true))
|
|
115
|
+
* )
|
|
116
|
+
* const { data, collection } = useLiveQuery(myLiveQuery)
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* // Access collection methods directly
|
|
120
|
+
* const { data, collection, isReady } = useLiveQuery(existingCollection)
|
|
121
|
+
*
|
|
122
|
+
* // Use collection for mutations
|
|
123
|
+
* const handleToggle = (id) => {
|
|
124
|
+
* collection.update(id, draft => { draft.completed = !draft.completed })
|
|
125
|
+
* }
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* // Handle states consistently
|
|
129
|
+
* const { data, isLoading, isError } = useLiveQuery(sharedCollection)
|
|
130
|
+
*
|
|
131
|
+
* if (isLoading) return <div>Loading...</div>
|
|
132
|
+
* if (isError) return <div>Error loading data</div>
|
|
133
|
+
*
|
|
134
|
+
* return <div>{data.map(item => <Item key={item.id} {...item} />)}</div>
|
|
135
|
+
*/
|
|
24
136
|
export declare function useLiveQuery<TResult extends object, TKey extends string | number, TUtils extends Record<string, any>>(liveQueryCollection: Collection<TResult, TKey, TUtils>): {
|
|
25
137
|
state: Map<TKey, TResult>;
|
|
26
138
|
data: Array<TResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLiveQuery.js","sources":["../../src/useLiveQuery.ts"],"sourcesContent":["import { useRef, useSyncExternalStore } from \"react\"\nimport { createLiveQueryCollection } from \"@tanstack/db\"\nimport type {\n Collection,\n CollectionStatus,\n Context,\n GetResult,\n InitialQueryBuilder,\n LiveQueryCollectionConfig,\n QueryBuilder,\n} from \"@tanstack/db\"\n\n// Overload 1: Accept just the query function\nexport function useLiveQuery<TContext extends Context>(\n queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>,\n deps?: Array<unknown>\n): {\n state: Map<string | number, GetResult<TContext>>\n data: Array<GetResult<TContext>>\n collection: Collection<GetResult<TContext>, string | number, {}>\n status: CollectionStatus\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n// Overload 2: Accept config object\nexport function useLiveQuery<TContext extends Context>(\n config: LiveQueryCollectionConfig<TContext>,\n deps?: Array<unknown>\n): {\n state: Map<string | number, GetResult<TContext>>\n data: Array<GetResult<TContext>>\n collection: Collection<GetResult<TContext>, string | number, {}>\n status: CollectionStatus\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n// Overload 3: Accept pre-created live query collection\nexport function useLiveQuery<\n TResult extends object,\n TKey extends string | number,\n TUtils extends Record<string, any>,\n>(\n liveQueryCollection: Collection<TResult, TKey, TUtils>\n): {\n state: Map<TKey, TResult>\n data: Array<TResult>\n collection: Collection<TResult, TKey, TUtils>\n status: CollectionStatus\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n// Implementation - use function overloads to infer the actual collection type\nexport function useLiveQuery(\n configOrQueryOrCollection: any,\n deps: Array<unknown> = []\n) {\n // Check if it's already a collection by checking for specific collection methods\n const isCollection =\n configOrQueryOrCollection &&\n typeof configOrQueryOrCollection === `object` &&\n typeof configOrQueryOrCollection.subscribeChanges === `function` &&\n typeof configOrQueryOrCollection.startSyncImmediate === `function` &&\n typeof configOrQueryOrCollection.id === `string`\n\n // Use refs to cache collection and track dependencies\n const collectionRef = useRef<any>(null)\n const depsRef = useRef<Array<unknown> | null>(null)\n const configRef = useRef<any>(null)\n\n // Check if we need to create/recreate the collection\n const needsNewCollection =\n !collectionRef.current ||\n (isCollection && configRef.current !== configOrQueryOrCollection) ||\n (!isCollection &&\n (depsRef.current === null ||\n depsRef.current.length !== deps.length ||\n depsRef.current.some((dep, i) => dep !== deps[i])))\n\n if (needsNewCollection) {\n if (isCollection) {\n // It's already a collection, ensure sync is started for React hooks\n configOrQueryOrCollection.startSyncImmediate()\n collectionRef.current = configOrQueryOrCollection\n configRef.current = configOrQueryOrCollection\n } else {\n // Original logic for creating collections\n // Ensure we always start sync for React hooks\n if (typeof configOrQueryOrCollection === `function`) {\n collectionRef.current = createLiveQueryCollection({\n query: configOrQueryOrCollection,\n startSync: true,\n gcTime: 0, // Live queries created by useLiveQuery are cleaned up immediately\n })\n } else {\n collectionRef.current = createLiveQueryCollection({\n startSync: true,\n gcTime: 0, // Live queries created by useLiveQuery are cleaned up immediately\n ...configOrQueryOrCollection,\n })\n }\n depsRef.current = [...deps]\n }\n }\n\n // Use refs to track version and memoized snapshot\n const versionRef = useRef(0)\n const snapshotRef = useRef<{\n state: Map<any, any>\n data: Array<any>\n collection: Collection<any, any, any>\n _version: number\n } | null>(null)\n\n // Reset refs when collection changes\n if (needsNewCollection) {\n versionRef.current = 0\n snapshotRef.current = null\n }\n\n // Create stable subscribe function using ref\n const subscribeRef = useRef<\n ((onStoreChange: () => void) => () => void) | null\n >(null)\n if (!subscribeRef.current || needsNewCollection) {\n subscribeRef.current = (onStoreChange: () => void) => {\n const unsubscribe = collectionRef.current!.subscribeChanges(() => {\n versionRef.current += 1\n onStoreChange()\n })\n return () => {\n unsubscribe()\n }\n }\n }\n\n // Create stable getSnapshot function using ref\n const getSnapshotRef = useRef<\n | (() => {\n state: Map<any, any>\n data: Array<any>\n collection: Collection<any, any, any>\n })\n | null\n >(null)\n if (!getSnapshotRef.current || needsNewCollection) {\n getSnapshotRef.current = () => {\n const currentVersion = versionRef.current\n const currentCollection = collectionRef.current!\n\n // If we don't have a snapshot or the version changed, create a new one\n if (\n !snapshotRef.current ||\n snapshotRef.current._version !== currentVersion\n ) {\n snapshotRef.current = {\n get state() {\n return new Map(currentCollection.entries())\n },\n get data() {\n return Array.from(currentCollection.values())\n },\n collection: currentCollection,\n _version: currentVersion,\n }\n }\n\n return snapshotRef.current\n }\n }\n\n // Use useSyncExternalStore to subscribe to collection changes\n const snapshot = useSyncExternalStore(\n subscribeRef.current,\n getSnapshotRef.current\n )\n\n return {\n state: snapshot.state,\n data: snapshot.data,\n collection: snapshot.collection,\n status: snapshot.collection.status,\n isLoading:\n snapshot.collection.status === `loading` ||\n snapshot.collection.status === `initialCommit`,\n isReady: snapshot.collection.status === `ready`,\n isIdle: snapshot.collection.status === `idle`,\n isError: snapshot.collection.status === `error`,\n isCleanedUp: snapshot.collection.status === `cleaned-up`,\n }\n}\n"],"names":[],"mappings":";;AAgEO,SAAS,aACd,2BACA,OAAuB,IACvB;AAEA,QAAM,eACJ,6BACA,OAAO,8BAA8B,YACrC,OAAO,0BAA0B,qBAAqB,cACtD,OAAO,0BAA0B,uBAAuB,cACxD,OAAO,0BAA0B,OAAO;AAGpC,QAAA,gBAAgB,OAAY,IAAI;AAChC,QAAA,UAAU,OAA8B,IAAI;AAC5C,QAAA,YAAY,OAAY,IAAI;AAG5B,QAAA,qBACJ,CAAC,cAAc,WACd,gBAAgB,UAAU,YAAY,6BACtC,CAAC,iBACC,QAAQ,YAAY,QACnB,QAAQ,QAAQ,WAAW,KAAK,UAChC,QAAQ,QAAQ,KAAK,CAAC,KAAK,MAAM,QAAQ,KAAK,CAAC,CAAC;AAEtD,MAAI,oBAAoB;AACtB,QAAI,cAAc;AAEhB,gCAA0B,mBAAmB;AAC7C,oBAAc,UAAU;AACxB,gBAAU,UAAU;AAAA,IAAA,OACf;AAGD,UAAA,OAAO,8BAA8B,YAAY;AACnD,sBAAc,UAAU,0BAA0B;AAAA,UAChD,OAAO;AAAA,UACP,WAAW;AAAA,UACX,QAAQ;AAAA;AAAA,QAAA,CACT;AAAA,MAAA,OACI;AACL,sBAAc,UAAU,0BAA0B;AAAA,UAChD,WAAW;AAAA,UACX,QAAQ;AAAA;AAAA,UACR,GAAG;AAAA,QAAA,CACJ;AAAA,MAAA;AAEK,cAAA,UAAU,CAAC,GAAG,IAAI;AAAA,IAAA;AAAA,EAC5B;AAII,QAAA,aAAa,OAAO,CAAC;AACrB,QAAA,cAAc,OAKV,IAAI;AAGd,MAAI,oBAAoB;AACtB,eAAW,UAAU;AACrB,gBAAY,UAAU;AAAA,EAAA;AAIlB,QAAA,eAAe,OAEnB,IAAI;AACF,MAAA,CAAC,aAAa,WAAW,oBAAoB;AAClC,iBAAA,UAAU,CAAC,kBAA8B;AACpD,YAAM,cAAc,cAAc,QAAS,iBAAiB,MAAM;AAChE,mBAAW,WAAW;AACR,sBAAA;AAAA,MAAA,CACf;AACD,aAAO,MAAM;AACC,oBAAA;AAAA,MACd;AAAA,IACF;AAAA,EAAA;AAII,QAAA,iBAAiB,OAOrB,IAAI;AACF,MAAA,CAAC,eAAe,WAAW,oBAAoB;AACjD,mBAAe,UAAU,MAAM;AAC7B,YAAM,iBAAiB,WAAW;AAClC,YAAM,oBAAoB,cAAc;AAGxC,UACE,CAAC,YAAY,WACb,YAAY,QAAQ,aAAa,gBACjC;AACA,oBAAY,UAAU;AAAA,UACpB,IAAI,QAAQ;AACV,mBAAO,IAAI,IAAI,kBAAkB,SAAS;AAAA,UAC5C;AAAA,UACA,IAAI,OAAO;AACT,mBAAO,MAAM,KAAK,kBAAkB,OAAA,CAAQ;AAAA,UAC9C;AAAA,UACA,YAAY;AAAA,UACZ,UAAU;AAAA,QACZ;AAAA,MAAA;AAGF,aAAO,YAAY;AAAA,IACrB;AAAA,EAAA;AAIF,QAAM,WAAW;AAAA,IACf,aAAa;AAAA,IACb,eAAe;AAAA,EACjB;AAEO,SAAA;AAAA,IACL,OAAO,SAAS;AAAA,IAChB,MAAM,SAAS;AAAA,IACf,YAAY,SAAS;AAAA,IACrB,QAAQ,SAAS,WAAW;AAAA,IAC5B,WACE,SAAS,WAAW,WAAW,aAC/B,SAAS,WAAW,WAAW;AAAA,IACjC,SAAS,SAAS,WAAW,WAAW;AAAA,IACxC,QAAQ,SAAS,WAAW,WAAW;AAAA,IACvC,SAAS,SAAS,WAAW,WAAW;AAAA,IACxC,aAAa,SAAS,WAAW,WAAW;AAAA,EAC9C;AACF;"}
|
|
1
|
+
{"version":3,"file":"useLiveQuery.js","sources":["../../src/useLiveQuery.ts"],"sourcesContent":["import { useRef, useSyncExternalStore } from \"react\"\nimport { createLiveQueryCollection } from \"@tanstack/db\"\nimport type {\n Collection,\n CollectionStatus,\n Context,\n GetResult,\n InitialQueryBuilder,\n LiveQueryCollectionConfig,\n QueryBuilder,\n} from \"@tanstack/db\"\n\n/**\n * Create a live query using a query function\n * @param queryFn - Query function that defines what data to fetch\n * @param deps - Array of dependencies that trigger query re-execution when changed\n * @returns Object with reactive data, state, and status information\n * @example\n * // Basic query with object syntax\n * const { data, isLoading } = useLiveQuery((q) =>\n * q.from({ todos: todosCollection })\n * .where(({ todos }) => eq(todos.completed, false))\n * .select(({ todos }) => ({ id: todos.id, text: todos.text }))\n * )\n *\n * @example\n * // With dependencies that trigger re-execution\n * const { data, state } = useLiveQuery(\n * (q) => q.from({ todos: todosCollection })\n * .where(({ todos }) => gt(todos.priority, minPriority)),\n * [minPriority] // Re-run when minPriority changes\n * )\n *\n * @example\n * // Join pattern\n * const { data } = useLiveQuery((q) =>\n * q.from({ issues: issueCollection })\n * .join({ persons: personCollection }, ({ issues, persons }) =>\n * eq(issues.userId, persons.id)\n * )\n * .select(({ issues, persons }) => ({\n * id: issues.id,\n * title: issues.title,\n * userName: persons.name\n * }))\n * )\n *\n * @example\n * // Handle loading and error states\n * const { data, isLoading, isError, status } = useLiveQuery((q) =>\n * q.from({ todos: todoCollection })\n * )\n *\n * if (isLoading) return <div>Loading...</div>\n * if (isError) return <div>Error: {status}</div>\n *\n * return (\n * <ul>\n * {data.map(todo => <li key={todo.id}>{todo.text}</li>)}\n * </ul>\n * )\n */\n// Overload 1: Accept just the query function\nexport function useLiveQuery<TContext extends Context>(\n queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>,\n deps?: Array<unknown>\n): {\n state: Map<string | number, GetResult<TContext>>\n data: Array<GetResult<TContext>>\n collection: Collection<GetResult<TContext>, string | number, {}>\n status: CollectionStatus\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n/**\n * Create a live query using configuration object\n * @param config - Configuration object with query and options\n * @param deps - Array of dependencies that trigger query re-execution when changed\n * @returns Object with reactive data, state, and status information\n * @example\n * // Basic config object usage\n * const { data, status } = useLiveQuery({\n * query: (q) => q.from({ todos: todosCollection }),\n * gcTime: 60000\n * })\n *\n * @example\n * // With query builder and options\n * const queryBuilder = new Query()\n * .from({ persons: collection })\n * .where(({ persons }) => gt(persons.age, 30))\n * .select(({ persons }) => ({ id: persons.id, name: persons.name }))\n *\n * const { data, isReady } = useLiveQuery({ query: queryBuilder })\n *\n * @example\n * // Handle all states uniformly\n * const { data, isLoading, isReady, isError } = useLiveQuery({\n * query: (q) => q.from({ items: itemCollection })\n * })\n *\n * if (isLoading) return <div>Loading...</div>\n * if (isError) return <div>Something went wrong</div>\n * if (!isReady) return <div>Preparing...</div>\n *\n * return <div>{data.length} items loaded</div>\n */\n// Overload 2: Accept config object\nexport function useLiveQuery<TContext extends Context>(\n config: LiveQueryCollectionConfig<TContext>,\n deps?: Array<unknown>\n): {\n state: Map<string | number, GetResult<TContext>>\n data: Array<GetResult<TContext>>\n collection: Collection<GetResult<TContext>, string | number, {}>\n status: CollectionStatus\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n/**\n * Subscribe to an existing live query collection\n * @param liveQueryCollection - Pre-created live query collection to subscribe to\n * @returns Object with reactive data, state, and status information\n * @example\n * // Using pre-created live query collection\n * const myLiveQuery = createLiveQueryCollection((q) =>\n * q.from({ todos: todosCollection }).where(({ todos }) => eq(todos.active, true))\n * )\n * const { data, collection } = useLiveQuery(myLiveQuery)\n *\n * @example\n * // Access collection methods directly\n * const { data, collection, isReady } = useLiveQuery(existingCollection)\n *\n * // Use collection for mutations\n * const handleToggle = (id) => {\n * collection.update(id, draft => { draft.completed = !draft.completed })\n * }\n *\n * @example\n * // Handle states consistently\n * const { data, isLoading, isError } = useLiveQuery(sharedCollection)\n *\n * if (isLoading) return <div>Loading...</div>\n * if (isError) return <div>Error loading data</div>\n *\n * return <div>{data.map(item => <Item key={item.id} {...item} />)}</div>\n */\n// Overload 3: Accept pre-created live query collection\nexport function useLiveQuery<\n TResult extends object,\n TKey extends string | number,\n TUtils extends Record<string, any>,\n>(\n liveQueryCollection: Collection<TResult, TKey, TUtils>\n): {\n state: Map<TKey, TResult>\n data: Array<TResult>\n collection: Collection<TResult, TKey, TUtils>\n status: CollectionStatus\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n// Implementation - use function overloads to infer the actual collection type\nexport function useLiveQuery(\n configOrQueryOrCollection: any,\n deps: Array<unknown> = []\n) {\n // Check if it's already a collection by checking for specific collection methods\n const isCollection =\n configOrQueryOrCollection &&\n typeof configOrQueryOrCollection === `object` &&\n typeof configOrQueryOrCollection.subscribeChanges === `function` &&\n typeof configOrQueryOrCollection.startSyncImmediate === `function` &&\n typeof configOrQueryOrCollection.id === `string`\n\n // Use refs to cache collection and track dependencies\n const collectionRef = useRef<any>(null)\n const depsRef = useRef<Array<unknown> | null>(null)\n const configRef = useRef<any>(null)\n\n // Check if we need to create/recreate the collection\n const needsNewCollection =\n !collectionRef.current ||\n (isCollection && configRef.current !== configOrQueryOrCollection) ||\n (!isCollection &&\n (depsRef.current === null ||\n depsRef.current.length !== deps.length ||\n depsRef.current.some((dep, i) => dep !== deps[i])))\n\n if (needsNewCollection) {\n if (isCollection) {\n // It's already a collection, ensure sync is started for React hooks\n configOrQueryOrCollection.startSyncImmediate()\n collectionRef.current = configOrQueryOrCollection\n configRef.current = configOrQueryOrCollection\n } else {\n // Original logic for creating collections\n // Ensure we always start sync for React hooks\n if (typeof configOrQueryOrCollection === `function`) {\n collectionRef.current = createLiveQueryCollection({\n query: configOrQueryOrCollection,\n startSync: true,\n gcTime: 0, // Live queries created by useLiveQuery are cleaned up immediately\n })\n } else {\n collectionRef.current = createLiveQueryCollection({\n startSync: true,\n gcTime: 0, // Live queries created by useLiveQuery are cleaned up immediately\n ...configOrQueryOrCollection,\n })\n }\n depsRef.current = [...deps]\n }\n }\n\n // Use refs to track version and memoized snapshot\n const versionRef = useRef(0)\n const snapshotRef = useRef<{\n state: Map<any, any>\n data: Array<any>\n collection: Collection<any, any, any>\n _version: number\n } | null>(null)\n\n // Reset refs when collection changes\n if (needsNewCollection) {\n versionRef.current = 0\n snapshotRef.current = null\n }\n\n // Create stable subscribe function using ref\n const subscribeRef = useRef<\n ((onStoreChange: () => void) => () => void) | null\n >(null)\n if (!subscribeRef.current || needsNewCollection) {\n subscribeRef.current = (onStoreChange: () => void) => {\n const unsubscribe = collectionRef.current!.subscribeChanges(() => {\n versionRef.current += 1\n onStoreChange()\n })\n return () => {\n unsubscribe()\n }\n }\n }\n\n // Create stable getSnapshot function using ref\n const getSnapshotRef = useRef<\n | (() => {\n state: Map<any, any>\n data: Array<any>\n collection: Collection<any, any, any>\n })\n | null\n >(null)\n if (!getSnapshotRef.current || needsNewCollection) {\n getSnapshotRef.current = () => {\n const currentVersion = versionRef.current\n const currentCollection = collectionRef.current!\n\n // If we don't have a snapshot or the version changed, create a new one\n if (\n !snapshotRef.current ||\n snapshotRef.current._version !== currentVersion\n ) {\n snapshotRef.current = {\n get state() {\n return new Map(currentCollection.entries())\n },\n get data() {\n return Array.from(currentCollection.values())\n },\n collection: currentCollection,\n _version: currentVersion,\n }\n }\n\n return snapshotRef.current\n }\n }\n\n // Use useSyncExternalStore to subscribe to collection changes\n const snapshot = useSyncExternalStore(\n subscribeRef.current,\n getSnapshotRef.current\n )\n\n return {\n state: snapshot.state,\n data: snapshot.data,\n collection: snapshot.collection,\n status: snapshot.collection.status,\n isLoading:\n snapshot.collection.status === `loading` ||\n snapshot.collection.status === `initialCommit`,\n isReady: snapshot.collection.status === `ready`,\n isIdle: snapshot.collection.status === `idle`,\n isError: snapshot.collection.status === `error`,\n isCleanedUp: snapshot.collection.status === `cleaned-up`,\n }\n}\n"],"names":[],"mappings":";;AAgLO,SAAS,aACd,2BACA,OAAuB,IACvB;AAEA,QAAM,eACJ,6BACA,OAAO,8BAA8B,YACrC,OAAO,0BAA0B,qBAAqB,cACtD,OAAO,0BAA0B,uBAAuB,cACxD,OAAO,0BAA0B,OAAO;AAG1C,QAAM,gBAAgB,OAAY,IAAI;AACtC,QAAM,UAAU,OAA8B,IAAI;AAClD,QAAM,YAAY,OAAY,IAAI;AAGlC,QAAM,qBACJ,CAAC,cAAc,WACd,gBAAgB,UAAU,YAAY,6BACtC,CAAC,iBACC,QAAQ,YAAY,QACnB,QAAQ,QAAQ,WAAW,KAAK,UAChC,QAAQ,QAAQ,KAAK,CAAC,KAAK,MAAM,QAAQ,KAAK,CAAC,CAAC;AAEtD,MAAI,oBAAoB;AACtB,QAAI,cAAc;AAEhB,gCAA0B,mBAAA;AAC1B,oBAAc,UAAU;AACxB,gBAAU,UAAU;AAAA,IACtB,OAAO;AAGL,UAAI,OAAO,8BAA8B,YAAY;AACnD,sBAAc,UAAU,0BAA0B;AAAA,UAChD,OAAO;AAAA,UACP,WAAW;AAAA,UACX,QAAQ;AAAA;AAAA,QAAA,CACT;AAAA,MACH,OAAO;AACL,sBAAc,UAAU,0BAA0B;AAAA,UAChD,WAAW;AAAA,UACX,QAAQ;AAAA;AAAA,UACR,GAAG;AAAA,QAAA,CACJ;AAAA,MACH;AACA,cAAQ,UAAU,CAAC,GAAG,IAAI;AAAA,IAC5B;AAAA,EACF;AAGA,QAAM,aAAa,OAAO,CAAC;AAC3B,QAAM,cAAc,OAKV,IAAI;AAGd,MAAI,oBAAoB;AACtB,eAAW,UAAU;AACrB,gBAAY,UAAU;AAAA,EACxB;AAGA,QAAM,eAAe,OAEnB,IAAI;AACN,MAAI,CAAC,aAAa,WAAW,oBAAoB;AAC/C,iBAAa,UAAU,CAAC,kBAA8B;AACpD,YAAM,cAAc,cAAc,QAAS,iBAAiB,MAAM;AAChE,mBAAW,WAAW;AACtB,sBAAA;AAAA,MACF,CAAC;AACD,aAAO,MAAM;AACX,oBAAA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,QAAM,iBAAiB,OAOrB,IAAI;AACN,MAAI,CAAC,eAAe,WAAW,oBAAoB;AACjD,mBAAe,UAAU,MAAM;AAC7B,YAAM,iBAAiB,WAAW;AAClC,YAAM,oBAAoB,cAAc;AAGxC,UACE,CAAC,YAAY,WACb,YAAY,QAAQ,aAAa,gBACjC;AACA,oBAAY,UAAU;AAAA,UACpB,IAAI,QAAQ;AACV,mBAAO,IAAI,IAAI,kBAAkB,SAAS;AAAA,UAC5C;AAAA,UACA,IAAI,OAAO;AACT,mBAAO,MAAM,KAAK,kBAAkB,OAAA,CAAQ;AAAA,UAC9C;AAAA,UACA,YAAY;AAAA,UACZ,UAAU;AAAA,QAAA;AAAA,MAEd;AAEA,aAAO,YAAY;AAAA,IACrB;AAAA,EACF;AAGA,QAAM,WAAW;AAAA,IACf,aAAa;AAAA,IACb,eAAe;AAAA,EAAA;AAGjB,SAAO;AAAA,IACL,OAAO,SAAS;AAAA,IAChB,MAAM,SAAS;AAAA,IACf,YAAY,SAAS;AAAA,IACrB,QAAQ,SAAS,WAAW;AAAA,IAC5B,WACE,SAAS,WAAW,WAAW,aAC/B,SAAS,WAAW,WAAW;AAAA,IACjC,SAAS,SAAS,WAAW,WAAW;AAAA,IACxC,QAAQ,SAAS,WAAW,WAAW;AAAA,IACvC,SAAS,SAAS,WAAW,WAAW;AAAA,IACxC,aAAa,SAAS,WAAW,WAAW;AAAA,EAAA;AAEhD;"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/react-db",
|
|
3
3
|
"description": "React integration for @tanstack/db",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.19",
|
|
5
5
|
"author": "Kyle Mathews",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"use-sync-external-store": "^1.2.0",
|
|
20
|
-
"@tanstack/db": "0.0.
|
|
20
|
+
"@tanstack/db": "0.0.19"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@electric-sql/client": "1.0.0",
|
package/src/useLiveQuery.ts
CHANGED
|
@@ -10,6 +10,56 @@ import type {
|
|
|
10
10
|
QueryBuilder,
|
|
11
11
|
} from "@tanstack/db"
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Create a live query using a query function
|
|
15
|
+
* @param queryFn - Query function that defines what data to fetch
|
|
16
|
+
* @param deps - Array of dependencies that trigger query re-execution when changed
|
|
17
|
+
* @returns Object with reactive data, state, and status information
|
|
18
|
+
* @example
|
|
19
|
+
* // Basic query with object syntax
|
|
20
|
+
* const { data, isLoading } = useLiveQuery((q) =>
|
|
21
|
+
* q.from({ todos: todosCollection })
|
|
22
|
+
* .where(({ todos }) => eq(todos.completed, false))
|
|
23
|
+
* .select(({ todos }) => ({ id: todos.id, text: todos.text }))
|
|
24
|
+
* )
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // With dependencies that trigger re-execution
|
|
28
|
+
* const { data, state } = useLiveQuery(
|
|
29
|
+
* (q) => q.from({ todos: todosCollection })
|
|
30
|
+
* .where(({ todos }) => gt(todos.priority, minPriority)),
|
|
31
|
+
* [minPriority] // Re-run when minPriority changes
|
|
32
|
+
* )
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // Join pattern
|
|
36
|
+
* const { data } = useLiveQuery((q) =>
|
|
37
|
+
* q.from({ issues: issueCollection })
|
|
38
|
+
* .join({ persons: personCollection }, ({ issues, persons }) =>
|
|
39
|
+
* eq(issues.userId, persons.id)
|
|
40
|
+
* )
|
|
41
|
+
* .select(({ issues, persons }) => ({
|
|
42
|
+
* id: issues.id,
|
|
43
|
+
* title: issues.title,
|
|
44
|
+
* userName: persons.name
|
|
45
|
+
* }))
|
|
46
|
+
* )
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* // Handle loading and error states
|
|
50
|
+
* const { data, isLoading, isError, status } = useLiveQuery((q) =>
|
|
51
|
+
* q.from({ todos: todoCollection })
|
|
52
|
+
* )
|
|
53
|
+
*
|
|
54
|
+
* if (isLoading) return <div>Loading...</div>
|
|
55
|
+
* if (isError) return <div>Error: {status}</div>
|
|
56
|
+
*
|
|
57
|
+
* return (
|
|
58
|
+
* <ul>
|
|
59
|
+
* {data.map(todo => <li key={todo.id}>{todo.text}</li>)}
|
|
60
|
+
* </ul>
|
|
61
|
+
* )
|
|
62
|
+
*/
|
|
13
63
|
// Overload 1: Accept just the query function
|
|
14
64
|
export function useLiveQuery<TContext extends Context>(
|
|
15
65
|
queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>,
|
|
@@ -26,6 +76,39 @@ export function useLiveQuery<TContext extends Context>(
|
|
|
26
76
|
isCleanedUp: boolean
|
|
27
77
|
}
|
|
28
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Create a live query using configuration object
|
|
81
|
+
* @param config - Configuration object with query and options
|
|
82
|
+
* @param deps - Array of dependencies that trigger query re-execution when changed
|
|
83
|
+
* @returns Object with reactive data, state, and status information
|
|
84
|
+
* @example
|
|
85
|
+
* // Basic config object usage
|
|
86
|
+
* const { data, status } = useLiveQuery({
|
|
87
|
+
* query: (q) => q.from({ todos: todosCollection }),
|
|
88
|
+
* gcTime: 60000
|
|
89
|
+
* })
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* // With query builder and options
|
|
93
|
+
* const queryBuilder = new Query()
|
|
94
|
+
* .from({ persons: collection })
|
|
95
|
+
* .where(({ persons }) => gt(persons.age, 30))
|
|
96
|
+
* .select(({ persons }) => ({ id: persons.id, name: persons.name }))
|
|
97
|
+
*
|
|
98
|
+
* const { data, isReady } = useLiveQuery({ query: queryBuilder })
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* // Handle all states uniformly
|
|
102
|
+
* const { data, isLoading, isReady, isError } = useLiveQuery({
|
|
103
|
+
* query: (q) => q.from({ items: itemCollection })
|
|
104
|
+
* })
|
|
105
|
+
*
|
|
106
|
+
* if (isLoading) return <div>Loading...</div>
|
|
107
|
+
* if (isError) return <div>Something went wrong</div>
|
|
108
|
+
* if (!isReady) return <div>Preparing...</div>
|
|
109
|
+
*
|
|
110
|
+
* return <div>{data.length} items loaded</div>
|
|
111
|
+
*/
|
|
29
112
|
// Overload 2: Accept config object
|
|
30
113
|
export function useLiveQuery<TContext extends Context>(
|
|
31
114
|
config: LiveQueryCollectionConfig<TContext>,
|
|
@@ -42,6 +125,35 @@ export function useLiveQuery<TContext extends Context>(
|
|
|
42
125
|
isCleanedUp: boolean
|
|
43
126
|
}
|
|
44
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Subscribe to an existing live query collection
|
|
130
|
+
* @param liveQueryCollection - Pre-created live query collection to subscribe to
|
|
131
|
+
* @returns Object with reactive data, state, and status information
|
|
132
|
+
* @example
|
|
133
|
+
* // Using pre-created live query collection
|
|
134
|
+
* const myLiveQuery = createLiveQueryCollection((q) =>
|
|
135
|
+
* q.from({ todos: todosCollection }).where(({ todos }) => eq(todos.active, true))
|
|
136
|
+
* )
|
|
137
|
+
* const { data, collection } = useLiveQuery(myLiveQuery)
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* // Access collection methods directly
|
|
141
|
+
* const { data, collection, isReady } = useLiveQuery(existingCollection)
|
|
142
|
+
*
|
|
143
|
+
* // Use collection for mutations
|
|
144
|
+
* const handleToggle = (id) => {
|
|
145
|
+
* collection.update(id, draft => { draft.completed = !draft.completed })
|
|
146
|
+
* }
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* // Handle states consistently
|
|
150
|
+
* const { data, isLoading, isError } = useLiveQuery(sharedCollection)
|
|
151
|
+
*
|
|
152
|
+
* if (isLoading) return <div>Loading...</div>
|
|
153
|
+
* if (isError) return <div>Error loading data</div>
|
|
154
|
+
*
|
|
155
|
+
* return <div>{data.map(item => <Item key={item.id} {...item} />)}</div>
|
|
156
|
+
*/
|
|
45
157
|
// Overload 3: Accept pre-created live query collection
|
|
46
158
|
export function useLiveQuery<
|
|
47
159
|
TResult extends object,
|