@tanstack/react-db 0.0.14 → 0.0.15
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.
- package/dist/cjs/useLiveQuery.cjs +73 -22
- package/dist/cjs/useLiveQuery.cjs.map +1 -1
- package/dist/cjs/useLiveQuery.d.cts +16 -7
- package/dist/esm/useLiveQuery.d.ts +16 -7
- package/dist/esm/useLiveQuery.js +75 -24
- package/dist/esm/useLiveQuery.js.map +1 -1
- package/package.json +2 -3
- package/src/useLiveQuery.ts +155 -37
|
@@ -1,32 +1,83 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const react = require("react");
|
|
4
|
-
const reactStore = require("@tanstack/react-store");
|
|
5
4
|
const db = require("@tanstack/db");
|
|
6
|
-
function useLiveQuery(
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
5
|
+
function useLiveQuery(configOrQueryOrCollection, deps = []) {
|
|
6
|
+
const isCollection = configOrQueryOrCollection && typeof configOrQueryOrCollection === `object` && typeof configOrQueryOrCollection.subscribeChanges === `function` && typeof configOrQueryOrCollection.startSyncImmediate === `function` && typeof configOrQueryOrCollection.id === `string`;
|
|
7
|
+
const collectionRef = react.useRef(null);
|
|
8
|
+
const depsRef = react.useRef(null);
|
|
9
|
+
const configRef = react.useRef(null);
|
|
10
|
+
const needsNewCollection = !collectionRef.current || isCollection && configRef.current !== configOrQueryOrCollection || !isCollection && (depsRef.current === null || depsRef.current.length !== deps.length || depsRef.current.some((dep, i) => dep !== deps[i]));
|
|
11
|
+
if (needsNewCollection) {
|
|
12
|
+
if (isCollection) {
|
|
13
|
+
configOrQueryOrCollection.startSyncImmediate();
|
|
14
|
+
collectionRef.current = configOrQueryOrCollection;
|
|
15
|
+
configRef.current = configOrQueryOrCollection;
|
|
16
|
+
} else {
|
|
17
|
+
if (typeof configOrQueryOrCollection === `function`) {
|
|
18
|
+
collectionRef.current = db.createLiveQueryCollection({
|
|
19
|
+
query: configOrQueryOrCollection,
|
|
20
|
+
startSync: true,
|
|
21
|
+
gcTime: 0
|
|
22
|
+
// Live queries created by useLiveQuery are cleaned up immediately
|
|
23
|
+
});
|
|
24
|
+
} else {
|
|
25
|
+
collectionRef.current = db.createLiveQueryCollection({
|
|
26
|
+
startSync: true,
|
|
27
|
+
gcTime: 0,
|
|
28
|
+
// Live queries created by useLiveQuery are cleaned up immediately
|
|
29
|
+
...configOrQueryOrCollection
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
depsRef.current = [...deps];
|
|
21
33
|
}
|
|
22
|
-
|
|
23
|
-
|
|
34
|
+
}
|
|
35
|
+
const versionRef = react.useRef(0);
|
|
36
|
+
const snapshotRef = react.useRef(null);
|
|
37
|
+
if (needsNewCollection) {
|
|
38
|
+
versionRef.current = 0;
|
|
39
|
+
snapshotRef.current = null;
|
|
40
|
+
}
|
|
41
|
+
const subscribeRef = react.useRef(null);
|
|
42
|
+
if (!subscribeRef.current || needsNewCollection) {
|
|
43
|
+
subscribeRef.current = (onStoreChange) => {
|
|
44
|
+
const unsubscribe = collectionRef.current.subscribeChanges(() => {
|
|
45
|
+
versionRef.current += 1;
|
|
46
|
+
onStoreChange();
|
|
47
|
+
});
|
|
48
|
+
return () => {
|
|
49
|
+
unsubscribe();
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const getSnapshotRef = react.useRef(null);
|
|
54
|
+
if (!getSnapshotRef.current || needsNewCollection) {
|
|
55
|
+
getSnapshotRef.current = () => {
|
|
56
|
+
const currentVersion = versionRef.current;
|
|
57
|
+
const currentCollection = collectionRef.current;
|
|
58
|
+
if (!snapshotRef.current || snapshotRef.current._version !== currentVersion) {
|
|
59
|
+
snapshotRef.current = {
|
|
60
|
+
get state() {
|
|
61
|
+
return new Map(currentCollection.entries());
|
|
62
|
+
},
|
|
63
|
+
get data() {
|
|
64
|
+
return Array.from(currentCollection.values());
|
|
65
|
+
},
|
|
66
|
+
collection: currentCollection,
|
|
67
|
+
_version: currentVersion
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return snapshotRef.current;
|
|
24
71
|
};
|
|
25
|
-
}
|
|
72
|
+
}
|
|
73
|
+
const snapshot = react.useSyncExternalStore(
|
|
74
|
+
subscribeRef.current,
|
|
75
|
+
getSnapshotRef.current
|
|
76
|
+
);
|
|
26
77
|
return {
|
|
27
|
-
state,
|
|
28
|
-
data,
|
|
29
|
-
collection:
|
|
78
|
+
state: snapshot.state,
|
|
79
|
+
data: snapshot.data,
|
|
80
|
+
collection: snapshot.collection
|
|
30
81
|
};
|
|
31
82
|
}
|
|
32
83
|
exports.useLiveQuery = useLiveQuery;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLiveQuery.cjs","sources":["../../src/useLiveQuery.ts"],"sourcesContent":["import {
|
|
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 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}\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}\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}\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 }\n}\n"],"names":["useRef","createLiveQueryCollection","useSyncExternalStore"],"mappings":";;;;AA6CO,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,EACvB;AACF;;"}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import { Collection, Context,
|
|
2
|
-
export
|
|
3
|
-
state: Map<string | number,
|
|
4
|
-
data: Array<
|
|
5
|
-
collection: Collection<
|
|
6
|
-
}
|
|
7
|
-
export declare function useLiveQuery<
|
|
1
|
+
import { Collection, Context, GetResult, InitialQueryBuilder, LiveQueryCollectionConfig, QueryBuilder } from '@tanstack/db';
|
|
2
|
+
export declare function useLiveQuery<TContext extends Context>(queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>, deps?: Array<unknown>): {
|
|
3
|
+
state: Map<string | number, GetResult<TContext>>;
|
|
4
|
+
data: Array<GetResult<TContext>>;
|
|
5
|
+
collection: Collection<GetResult<TContext>, string | number, {}>;
|
|
6
|
+
};
|
|
7
|
+
export declare function useLiveQuery<TContext extends Context>(config: LiveQueryCollectionConfig<TContext>, deps?: Array<unknown>): {
|
|
8
|
+
state: Map<string | number, GetResult<TContext>>;
|
|
9
|
+
data: Array<GetResult<TContext>>;
|
|
10
|
+
collection: Collection<GetResult<TContext>, string | number, {}>;
|
|
11
|
+
};
|
|
12
|
+
export declare function useLiveQuery<TResult extends object, TKey extends string | number, TUtils extends Record<string, any>>(liveQueryCollection: Collection<TResult, TKey, TUtils>): {
|
|
13
|
+
state: Map<TKey, TResult>;
|
|
14
|
+
data: Array<TResult>;
|
|
15
|
+
collection: Collection<TResult, TKey, TUtils>;
|
|
16
|
+
};
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import { Collection, Context,
|
|
2
|
-
export
|
|
3
|
-
state: Map<string | number,
|
|
4
|
-
data: Array<
|
|
5
|
-
collection: Collection<
|
|
6
|
-
}
|
|
7
|
-
export declare function useLiveQuery<
|
|
1
|
+
import { Collection, Context, GetResult, InitialQueryBuilder, LiveQueryCollectionConfig, QueryBuilder } from '@tanstack/db';
|
|
2
|
+
export declare function useLiveQuery<TContext extends Context>(queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>, deps?: Array<unknown>): {
|
|
3
|
+
state: Map<string | number, GetResult<TContext>>;
|
|
4
|
+
data: Array<GetResult<TContext>>;
|
|
5
|
+
collection: Collection<GetResult<TContext>, string | number, {}>;
|
|
6
|
+
};
|
|
7
|
+
export declare function useLiveQuery<TContext extends Context>(config: LiveQueryCollectionConfig<TContext>, deps?: Array<unknown>): {
|
|
8
|
+
state: Map<string | number, GetResult<TContext>>;
|
|
9
|
+
data: Array<GetResult<TContext>>;
|
|
10
|
+
collection: Collection<GetResult<TContext>, string | number, {}>;
|
|
11
|
+
};
|
|
12
|
+
export declare function useLiveQuery<TResult extends object, TKey extends string | number, TUtils extends Record<string, any>>(liveQueryCollection: Collection<TResult, TKey, TUtils>): {
|
|
13
|
+
state: Map<TKey, TResult>;
|
|
14
|
+
data: Array<TResult>;
|
|
15
|
+
collection: Collection<TResult, TKey, TUtils>;
|
|
16
|
+
};
|
package/dist/esm/useLiveQuery.js
CHANGED
|
@@ -1,30 +1,81 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
function
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
import { useRef, useSyncExternalStore } from "react";
|
|
2
|
+
import { createLiveQueryCollection } from "@tanstack/db";
|
|
3
|
+
function useLiveQuery(configOrQueryOrCollection, deps = []) {
|
|
4
|
+
const isCollection = configOrQueryOrCollection && typeof configOrQueryOrCollection === `object` && typeof configOrQueryOrCollection.subscribeChanges === `function` && typeof configOrQueryOrCollection.startSyncImmediate === `function` && typeof configOrQueryOrCollection.id === `string`;
|
|
5
|
+
const collectionRef = useRef(null);
|
|
6
|
+
const depsRef = useRef(null);
|
|
7
|
+
const configRef = useRef(null);
|
|
8
|
+
const needsNewCollection = !collectionRef.current || isCollection && configRef.current !== configOrQueryOrCollection || !isCollection && (depsRef.current === null || depsRef.current.length !== deps.length || depsRef.current.some((dep, i) => dep !== deps[i]));
|
|
9
|
+
if (needsNewCollection) {
|
|
10
|
+
if (isCollection) {
|
|
11
|
+
configOrQueryOrCollection.startSyncImmediate();
|
|
12
|
+
collectionRef.current = configOrQueryOrCollection;
|
|
13
|
+
configRef.current = configOrQueryOrCollection;
|
|
14
|
+
} else {
|
|
15
|
+
if (typeof configOrQueryOrCollection === `function`) {
|
|
16
|
+
collectionRef.current = createLiveQueryCollection({
|
|
17
|
+
query: configOrQueryOrCollection,
|
|
18
|
+
startSync: true,
|
|
19
|
+
gcTime: 0
|
|
20
|
+
// Live queries created by useLiveQuery are cleaned up immediately
|
|
21
|
+
});
|
|
22
|
+
} else {
|
|
23
|
+
collectionRef.current = createLiveQueryCollection({
|
|
24
|
+
startSync: true,
|
|
25
|
+
gcTime: 0,
|
|
26
|
+
// Live queries created by useLiveQuery are cleaned up immediately
|
|
27
|
+
...configOrQueryOrCollection
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
depsRef.current = [...deps];
|
|
19
31
|
}
|
|
20
|
-
|
|
21
|
-
|
|
32
|
+
}
|
|
33
|
+
const versionRef = useRef(0);
|
|
34
|
+
const snapshotRef = useRef(null);
|
|
35
|
+
if (needsNewCollection) {
|
|
36
|
+
versionRef.current = 0;
|
|
37
|
+
snapshotRef.current = null;
|
|
38
|
+
}
|
|
39
|
+
const subscribeRef = useRef(null);
|
|
40
|
+
if (!subscribeRef.current || needsNewCollection) {
|
|
41
|
+
subscribeRef.current = (onStoreChange) => {
|
|
42
|
+
const unsubscribe = collectionRef.current.subscribeChanges(() => {
|
|
43
|
+
versionRef.current += 1;
|
|
44
|
+
onStoreChange();
|
|
45
|
+
});
|
|
46
|
+
return () => {
|
|
47
|
+
unsubscribe();
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const getSnapshotRef = useRef(null);
|
|
52
|
+
if (!getSnapshotRef.current || needsNewCollection) {
|
|
53
|
+
getSnapshotRef.current = () => {
|
|
54
|
+
const currentVersion = versionRef.current;
|
|
55
|
+
const currentCollection = collectionRef.current;
|
|
56
|
+
if (!snapshotRef.current || snapshotRef.current._version !== currentVersion) {
|
|
57
|
+
snapshotRef.current = {
|
|
58
|
+
get state() {
|
|
59
|
+
return new Map(currentCollection.entries());
|
|
60
|
+
},
|
|
61
|
+
get data() {
|
|
62
|
+
return Array.from(currentCollection.values());
|
|
63
|
+
},
|
|
64
|
+
collection: currentCollection,
|
|
65
|
+
_version: currentVersion
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return snapshotRef.current;
|
|
22
69
|
};
|
|
23
|
-
}
|
|
70
|
+
}
|
|
71
|
+
const snapshot = useSyncExternalStore(
|
|
72
|
+
subscribeRef.current,
|
|
73
|
+
getSnapshotRef.current
|
|
74
|
+
);
|
|
24
75
|
return {
|
|
25
|
-
state,
|
|
26
|
-
data,
|
|
27
|
-
collection:
|
|
76
|
+
state: snapshot.state,
|
|
77
|
+
data: snapshot.data,
|
|
78
|
+
collection: snapshot.collection
|
|
28
79
|
};
|
|
29
80
|
}
|
|
30
81
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLiveQuery.js","sources":["../../src/useLiveQuery.ts"],"sourcesContent":["import {
|
|
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 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}\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}\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}\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 }\n}\n"],"names":[],"mappings":";;AA6CO,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,EACvB;AACF;"}
|
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.15",
|
|
5
5
|
"author": "Kyle Mathews",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -16,9 +16,8 @@
|
|
|
16
16
|
"typescript"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@tanstack/react-store": "^0.7.0",
|
|
20
19
|
"use-sync-external-store": "^1.2.0",
|
|
21
|
-
"@tanstack/db": "0.0.
|
|
20
|
+
"@tanstack/db": "0.0.15"
|
|
22
21
|
},
|
|
23
22
|
"devDependencies": {
|
|
24
23
|
"@electric-sql/client": "1.0.0",
|
package/src/useLiveQuery.ts
CHANGED
|
@@ -1,57 +1,175 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { compileQuery, queryBuilder } from "@tanstack/db"
|
|
1
|
+
import { useRef, useSyncExternalStore } from "react"
|
|
2
|
+
import { createLiveQueryCollection } from "@tanstack/db"
|
|
4
3
|
import type {
|
|
5
4
|
Collection,
|
|
6
5
|
Context,
|
|
6
|
+
GetResult,
|
|
7
7
|
InitialQueryBuilder,
|
|
8
|
+
LiveQueryCollectionConfig,
|
|
8
9
|
QueryBuilder,
|
|
9
|
-
ResultsFromContext,
|
|
10
|
-
Schema,
|
|
11
10
|
} from "@tanstack/db"
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
// Overload 1: Accept just the query function
|
|
13
|
+
export function useLiveQuery<TContext extends Context>(
|
|
14
|
+
queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>,
|
|
15
|
+
deps?: Array<unknown>
|
|
16
|
+
): {
|
|
17
|
+
state: Map<string | number, GetResult<TContext>>
|
|
18
|
+
data: Array<GetResult<TContext>>
|
|
19
|
+
collection: Collection<GetResult<TContext>, string | number, {}>
|
|
17
20
|
}
|
|
18
21
|
|
|
22
|
+
// Overload 2: Accept config object
|
|
23
|
+
export function useLiveQuery<TContext extends Context>(
|
|
24
|
+
config: LiveQueryCollectionConfig<TContext>,
|
|
25
|
+
deps?: Array<unknown>
|
|
26
|
+
): {
|
|
27
|
+
state: Map<string | number, GetResult<TContext>>
|
|
28
|
+
data: Array<GetResult<TContext>>
|
|
29
|
+
collection: Collection<GetResult<TContext>, string | number, {}>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Overload 3: Accept pre-created live query collection
|
|
19
33
|
export function useLiveQuery<
|
|
20
|
-
|
|
34
|
+
TResult extends object,
|
|
35
|
+
TKey extends string | number,
|
|
36
|
+
TUtils extends Record<string, any>,
|
|
21
37
|
>(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
38
|
+
liveQueryCollection: Collection<TResult, TKey, TUtils>
|
|
39
|
+
): {
|
|
40
|
+
state: Map<TKey, TResult>
|
|
41
|
+
data: Array<TResult>
|
|
42
|
+
collection: Collection<TResult, TKey, TUtils>
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Implementation - use function overloads to infer the actual collection type
|
|
46
|
+
export function useLiveQuery(
|
|
47
|
+
configOrQueryOrCollection: any,
|
|
25
48
|
deps: Array<unknown> = []
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
) {
|
|
50
|
+
// Check if it's already a collection by checking for specific collection methods
|
|
51
|
+
const isCollection =
|
|
52
|
+
configOrQueryOrCollection &&
|
|
53
|
+
typeof configOrQueryOrCollection === `object` &&
|
|
54
|
+
typeof configOrQueryOrCollection.subscribeChanges === `function` &&
|
|
55
|
+
typeof configOrQueryOrCollection.startSyncImmediate === `function` &&
|
|
56
|
+
typeof configOrQueryOrCollection.id === `string`
|
|
57
|
+
|
|
58
|
+
// Use refs to cache collection and track dependencies
|
|
59
|
+
const collectionRef = useRef<any>(null)
|
|
60
|
+
const depsRef = useRef<Array<unknown> | null>(null)
|
|
61
|
+
const configRef = useRef<any>(null)
|
|
62
|
+
|
|
63
|
+
// Check if we need to create/recreate the collection
|
|
64
|
+
const needsNewCollection =
|
|
65
|
+
!collectionRef.current ||
|
|
66
|
+
(isCollection && configRef.current !== configOrQueryOrCollection) ||
|
|
67
|
+
(!isCollection &&
|
|
68
|
+
(depsRef.current === null ||
|
|
69
|
+
depsRef.current.length !== deps.length ||
|
|
70
|
+
depsRef.current.some((dep, i) => dep !== deps[i])))
|
|
71
|
+
|
|
72
|
+
if (needsNewCollection) {
|
|
73
|
+
if (isCollection) {
|
|
74
|
+
// It's already a collection, ensure sync is started for React hooks
|
|
75
|
+
configOrQueryOrCollection.startSyncImmediate()
|
|
76
|
+
collectionRef.current = configOrQueryOrCollection
|
|
77
|
+
configRef.current = configOrQueryOrCollection
|
|
78
|
+
} else {
|
|
79
|
+
// Original logic for creating collections
|
|
80
|
+
// Ensure we always start sync for React hooks
|
|
81
|
+
if (typeof configOrQueryOrCollection === `function`) {
|
|
82
|
+
collectionRef.current = createLiveQueryCollection({
|
|
83
|
+
query: configOrQueryOrCollection,
|
|
84
|
+
startSync: true,
|
|
85
|
+
gcTime: 0, // Live queries created by useLiveQuery are cleaned up immediately
|
|
86
|
+
})
|
|
87
|
+
} else {
|
|
88
|
+
collectionRef.current = createLiveQueryCollection({
|
|
89
|
+
startSync: true,
|
|
90
|
+
gcTime: 0, // Live queries created by useLiveQuery are cleaned up immediately
|
|
91
|
+
...configOrQueryOrCollection,
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
depsRef.current = [...deps]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Use refs to track version and memoized snapshot
|
|
99
|
+
const versionRef = useRef(0)
|
|
100
|
+
const snapshotRef = useRef<{
|
|
101
|
+
state: Map<any, any>
|
|
102
|
+
data: Array<any>
|
|
103
|
+
collection: Collection<any, any, any>
|
|
104
|
+
_version: number
|
|
105
|
+
} | null>(null)
|
|
106
|
+
|
|
107
|
+
// Reset refs when collection changes
|
|
108
|
+
if (needsNewCollection) {
|
|
109
|
+
versionRef.current = 0
|
|
110
|
+
snapshotRef.current = null
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Create stable subscribe function using ref
|
|
114
|
+
const subscribeRef = useRef<
|
|
115
|
+
((onStoreChange: () => void) => () => void) | null
|
|
116
|
+
>(null)
|
|
117
|
+
if (!subscribeRef.current || needsNewCollection) {
|
|
118
|
+
subscribeRef.current = (onStoreChange: () => void) => {
|
|
119
|
+
const unsubscribe = collectionRef.current!.subscribeChanges(() => {
|
|
120
|
+
versionRef.current += 1
|
|
121
|
+
onStoreChange()
|
|
44
122
|
})
|
|
123
|
+
return () => {
|
|
124
|
+
unsubscribe()
|
|
125
|
+
}
|
|
45
126
|
}
|
|
127
|
+
}
|
|
46
128
|
|
|
47
|
-
|
|
48
|
-
|
|
129
|
+
// Create stable getSnapshot function using ref
|
|
130
|
+
const getSnapshotRef = useRef<
|
|
131
|
+
| (() => {
|
|
132
|
+
state: Map<any, any>
|
|
133
|
+
data: Array<any>
|
|
134
|
+
collection: Collection<any, any, any>
|
|
135
|
+
})
|
|
136
|
+
| null
|
|
137
|
+
>(null)
|
|
138
|
+
if (!getSnapshotRef.current || needsNewCollection) {
|
|
139
|
+
getSnapshotRef.current = () => {
|
|
140
|
+
const currentVersion = versionRef.current
|
|
141
|
+
const currentCollection = collectionRef.current!
|
|
142
|
+
|
|
143
|
+
// If we don't have a snapshot or the version changed, create a new one
|
|
144
|
+
if (
|
|
145
|
+
!snapshotRef.current ||
|
|
146
|
+
snapshotRef.current._version !== currentVersion
|
|
147
|
+
) {
|
|
148
|
+
snapshotRef.current = {
|
|
149
|
+
get state() {
|
|
150
|
+
return new Map(currentCollection.entries())
|
|
151
|
+
},
|
|
152
|
+
get data() {
|
|
153
|
+
return Array.from(currentCollection.values())
|
|
154
|
+
},
|
|
155
|
+
collection: currentCollection,
|
|
156
|
+
_version: currentVersion,
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return snapshotRef.current
|
|
49
161
|
}
|
|
50
|
-
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Use useSyncExternalStore to subscribe to collection changes
|
|
165
|
+
const snapshot = useSyncExternalStore(
|
|
166
|
+
subscribeRef.current,
|
|
167
|
+
getSnapshotRef.current
|
|
168
|
+
)
|
|
51
169
|
|
|
52
170
|
return {
|
|
53
|
-
state,
|
|
54
|
-
data,
|
|
55
|
-
collection:
|
|
171
|
+
state: snapshot.state,
|
|
172
|
+
data: snapshot.data,
|
|
173
|
+
collection: snapshot.collection,
|
|
56
174
|
}
|
|
57
175
|
}
|