@linkedrecords/react 0.6.0 → 0.7.0
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/index.d.mts +14 -9
- package/dist/index.d.ts +14 -9
- package/dist/index.js +41 -49
- package/dist/index.mjs +42 -50
- package/package.json +3 -3
- package/src/LinkedRecordsProvider.tsx +4 -2
- package/src/useRecords.ts +64 -71
package/dist/index.d.mts
CHANGED
|
@@ -26,18 +26,23 @@ interface LinkedRecordsProviderProps {
|
|
|
26
26
|
}
|
|
27
27
|
declare function LinkedRecordsProvider({ children, serverUrl, config }: LinkedRecordsProviderProps): react_jsx_runtime.JSX.Element;
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
[key: string]:
|
|
31
|
-
}
|
|
32
|
-
|
|
29
|
+
type JSONValue = string | number | boolean | null | JSONValue[] | {
|
|
30
|
+
[key: string]: JSONValue;
|
|
31
|
+
};
|
|
32
|
+
type KVValue = {
|
|
33
|
+
[key: string]: JSONValue | undefined;
|
|
34
|
+
};
|
|
35
|
+
type KVRecord<T extends object = KVValue> = T & {
|
|
33
36
|
_id?: string;
|
|
34
37
|
_createdAt?: Date;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
};
|
|
39
|
+
type CompoundRecordQuery = Parameters<LinkedRecords['Record']['subscribeToQuery']>[0];
|
|
40
|
+
type RecordQueryClause = Exclude<CompoundRecordQuery[string], string>[number];
|
|
41
|
+
declare function useKeyValueRecordsWithStatus<T extends object = KVValue>(query: RecordQueryClause[]): {
|
|
42
|
+
records: KVRecord<T>[];
|
|
38
43
|
isLoading: boolean;
|
|
39
44
|
};
|
|
40
|
-
declare function useKeyValueRecords(query:
|
|
45
|
+
declare function useKeyValueRecords<T extends object = KVValue>(query: RecordQueryClause[]): KVRecord<T>[];
|
|
41
46
|
/** @deprecated Use useKeyValueRecords instead. */
|
|
42
47
|
declare const useKeyValueAttributes: typeof useKeyValueRecords;
|
|
43
48
|
|
|
@@ -57,4 +62,4 @@ declare function useUserInfo(): UserInfo | null;
|
|
|
57
62
|
|
|
58
63
|
declare function useActorId(): string | undefined;
|
|
59
64
|
|
|
60
|
-
export { type KVRecord, type LinkedRecordsConfig, LinkedRecordsContext, type LinkedRecordsContextType, LinkedRecordsProvider, useActorId, useFacts, useKeyValueAttributes, useKeyValueRecords, useKeyValueRecordsWithStatus, useLinkedRecords, useUserInfo };
|
|
65
|
+
export { type KVRecord, type LinkedRecordsConfig, LinkedRecordsContext, type LinkedRecordsContextType, LinkedRecordsProvider, type RecordQueryClause, useActorId, useFacts, useKeyValueAttributes, useKeyValueRecords, useKeyValueRecordsWithStatus, useLinkedRecords, useUserInfo };
|
package/dist/index.d.ts
CHANGED
|
@@ -26,18 +26,23 @@ interface LinkedRecordsProviderProps {
|
|
|
26
26
|
}
|
|
27
27
|
declare function LinkedRecordsProvider({ children, serverUrl, config }: LinkedRecordsProviderProps): react_jsx_runtime.JSX.Element;
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
[key: string]:
|
|
31
|
-
}
|
|
32
|
-
|
|
29
|
+
type JSONValue = string | number | boolean | null | JSONValue[] | {
|
|
30
|
+
[key: string]: JSONValue;
|
|
31
|
+
};
|
|
32
|
+
type KVValue = {
|
|
33
|
+
[key: string]: JSONValue | undefined;
|
|
34
|
+
};
|
|
35
|
+
type KVRecord<T extends object = KVValue> = T & {
|
|
33
36
|
_id?: string;
|
|
34
37
|
_createdAt?: Date;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
};
|
|
39
|
+
type CompoundRecordQuery = Parameters<LinkedRecords['Record']['subscribeToQuery']>[0];
|
|
40
|
+
type RecordQueryClause = Exclude<CompoundRecordQuery[string], string>[number];
|
|
41
|
+
declare function useKeyValueRecordsWithStatus<T extends object = KVValue>(query: RecordQueryClause[]): {
|
|
42
|
+
records: KVRecord<T>[];
|
|
38
43
|
isLoading: boolean;
|
|
39
44
|
};
|
|
40
|
-
declare function useKeyValueRecords(query:
|
|
45
|
+
declare function useKeyValueRecords<T extends object = KVValue>(query: RecordQueryClause[]): KVRecord<T>[];
|
|
41
46
|
/** @deprecated Use useKeyValueRecords instead. */
|
|
42
47
|
declare const useKeyValueAttributes: typeof useKeyValueRecords;
|
|
43
48
|
|
|
@@ -57,4 +62,4 @@ declare function useUserInfo(): UserInfo | null;
|
|
|
57
62
|
|
|
58
63
|
declare function useActorId(): string | undefined;
|
|
59
64
|
|
|
60
|
-
export { type KVRecord, type LinkedRecordsConfig, LinkedRecordsContext, type LinkedRecordsContextType, LinkedRecordsProvider, useActorId, useFacts, useKeyValueAttributes, useKeyValueRecords, useKeyValueRecordsWithStatus, useLinkedRecords, useUserInfo };
|
|
65
|
+
export { type KVRecord, type LinkedRecordsConfig, LinkedRecordsContext, type LinkedRecordsContextType, LinkedRecordsProvider, type RecordQueryClause, useActorId, useFacts, useKeyValueAttributes, useKeyValueRecords, useKeyValueRecordsWithStatus, useLinkedRecords, useUserInfo };
|
package/dist/index.js
CHANGED
|
@@ -66,11 +66,13 @@ function createClient(serverUrl, config) {
|
|
|
66
66
|
}
|
|
67
67
|
function LinkedRecordsProvider({ children, serverUrl, config }) {
|
|
68
68
|
const [lr] = (0, import_react2.useState)(() => config ? createClient(serverUrl, config) : import_browser.default.getPublicClient(serverUrl));
|
|
69
|
-
|
|
69
|
+
const value = (0, import_react2.useMemo)(() => ({ lr }), [lr]);
|
|
70
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkedRecordsContext.Provider, { value, children });
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
// src/useRecords.ts
|
|
73
74
|
var import_react4 = require("react");
|
|
75
|
+
var import_browser2 = require("@linkedrecords/browser");
|
|
74
76
|
|
|
75
77
|
// src/useLinkedRecords.ts
|
|
76
78
|
var import_react3 = require("react");
|
|
@@ -88,7 +90,6 @@ function getQueryKey(query) {
|
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
// src/useRecords.ts
|
|
91
|
-
var import_browser2 = require("@linkedrecords/browser");
|
|
92
93
|
function useKeyValueRecordsWithStatus(query) {
|
|
93
94
|
const { lr } = useLinkedRecords();
|
|
94
95
|
const [records, setRecords] = (0, import_react4.useState)([]);
|
|
@@ -97,62 +98,53 @@ function useKeyValueRecordsWithStatus(query) {
|
|
|
97
98
|
(0, import_react4.useEffect)(() => {
|
|
98
99
|
setIsLoading(true);
|
|
99
100
|
let cancelled = false;
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
101
|
+
const recordUnsubscribes = /* @__PURE__ */ new Map();
|
|
102
|
+
const toKVRecord = async (r) => ({
|
|
103
|
+
_id: r.id,
|
|
104
|
+
_createdAt: r.createdAt,
|
|
105
|
+
...await r.getValue()
|
|
106
|
+
});
|
|
107
|
+
const queryUnsubscribe = lr.Record.subscribeToQuery({
|
|
108
|
+
records: [
|
|
109
|
+
["$it", "$hasDataType", import_browser2.KeyValueRecord],
|
|
110
|
+
...query
|
|
111
|
+
]
|
|
112
|
+
}, async ({ records: records2 }) => {
|
|
113
|
+
const kvRecords = records2;
|
|
114
|
+
const values = await Promise.all(kvRecords.map(toKVRecord));
|
|
115
|
+
if (cancelled) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
setRecords(values);
|
|
119
|
+
setIsLoading(false);
|
|
120
|
+
const ids = new Set(kvRecords.map((r) => r.id));
|
|
121
|
+
recordUnsubscribes.forEach((unsubscribe, id) => {
|
|
122
|
+
if (!ids.has(id)) {
|
|
123
|
+
recordUnsubscribes.delete(id);
|
|
124
|
+
unsubscribe.then((fn) => fn());
|
|
110
125
|
}
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const queryUnsubscribe = lr.Record.subscribeToQuery({
|
|
115
|
-
records: [
|
|
116
|
-
["$it", "$hasDataType", import_browser2.KeyValueRecord],
|
|
117
|
-
...query
|
|
118
|
-
]
|
|
119
|
-
}, async ({ records: records2 }) => {
|
|
120
|
-
const values = await Promise.all(records2.map(async (r) => ({
|
|
121
|
-
_id: r.id,
|
|
122
|
-
_createdAt: r.createdAt,
|
|
123
|
-
...await r.getValue()
|
|
124
|
-
})));
|
|
125
|
-
if (cancelled) {
|
|
126
|
+
});
|
|
127
|
+
kvRecords.forEach((r) => {
|
|
128
|
+
if (recordUnsubscribes.has(r.id)) {
|
|
126
129
|
return;
|
|
127
130
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (subscribedRecordIds.has(r.id)) {
|
|
131
|
+
recordUnsubscribes.set(r.id, r.subscribe(async () => {
|
|
132
|
+
const newValue = await toKVRecord(r);
|
|
133
|
+
if (cancelled) {
|
|
132
134
|
return;
|
|
133
135
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if (cancelled) {
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
const newValue = await r.getValue();
|
|
140
|
-
if (cancelled) {
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
setRecords((prev) => prev.map(
|
|
144
|
-
(v) => v._id === r.id ? { _id: r.id, _createdAt: r.createdAt, ...newValue } : v
|
|
145
|
-
));
|
|
146
|
-
});
|
|
147
|
-
});
|
|
136
|
+
setRecords((prev) => prev.map((v) => v._id === r.id ? newValue : v));
|
|
137
|
+
}));
|
|
148
138
|
});
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
139
|
+
});
|
|
140
|
+
queryUnsubscribe.catch((err) => {
|
|
141
|
+
console.error("useKeyValueRecords: could not subscribe to query", err);
|
|
152
142
|
});
|
|
153
143
|
return () => {
|
|
154
144
|
cancelled = true;
|
|
155
|
-
|
|
145
|
+
queryUnsubscribe.then((unsubscribe) => unsubscribe(), () => {
|
|
146
|
+
});
|
|
147
|
+
recordUnsubscribes.forEach((unsubscribe) => unsubscribe.then((fn) => fn()));
|
|
156
148
|
};
|
|
157
149
|
}, [lr, queryKey]);
|
|
158
150
|
return { records, isLoading };
|
package/dist/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { createContext } from "react";
|
|
|
3
3
|
var LinkedRecordsContext = createContext(void 0);
|
|
4
4
|
|
|
5
5
|
// src/LinkedRecordsProvider.tsx
|
|
6
|
-
import { useState } from "react";
|
|
6
|
+
import { useMemo, useState } from "react";
|
|
7
7
|
import LinkedRecords from "@linkedrecords/browser";
|
|
8
8
|
import { jsx } from "react/jsx-runtime";
|
|
9
9
|
function createClient(serverUrl, config) {
|
|
@@ -22,11 +22,13 @@ function createClient(serverUrl, config) {
|
|
|
22
22
|
}
|
|
23
23
|
function LinkedRecordsProvider({ children, serverUrl, config }) {
|
|
24
24
|
const [lr] = useState(() => config ? createClient(serverUrl, config) : LinkedRecords.getPublicClient(serverUrl));
|
|
25
|
-
|
|
25
|
+
const value = useMemo(() => ({ lr }), [lr]);
|
|
26
|
+
return /* @__PURE__ */ jsx(LinkedRecordsContext.Provider, { value, children });
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
// src/useRecords.ts
|
|
29
30
|
import { useEffect, useState as useState2 } from "react";
|
|
31
|
+
import { KeyValueRecord } from "@linkedrecords/browser";
|
|
30
32
|
|
|
31
33
|
// src/useLinkedRecords.ts
|
|
32
34
|
import { useContext } from "react";
|
|
@@ -44,7 +46,6 @@ function getQueryKey(query) {
|
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
// src/useRecords.ts
|
|
47
|
-
import { KeyValueRecord } from "@linkedrecords/browser";
|
|
48
49
|
function useKeyValueRecordsWithStatus(query) {
|
|
49
50
|
const { lr } = useLinkedRecords();
|
|
50
51
|
const [records, setRecords] = useState2([]);
|
|
@@ -53,62 +54,53 @@ function useKeyValueRecordsWithStatus(query) {
|
|
|
53
54
|
useEffect(() => {
|
|
54
55
|
setIsLoading(true);
|
|
55
56
|
let cancelled = false;
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
57
|
+
const recordUnsubscribes = /* @__PURE__ */ new Map();
|
|
58
|
+
const toKVRecord = async (r) => ({
|
|
59
|
+
_id: r.id,
|
|
60
|
+
_createdAt: r.createdAt,
|
|
61
|
+
...await r.getValue()
|
|
62
|
+
});
|
|
63
|
+
const queryUnsubscribe = lr.Record.subscribeToQuery({
|
|
64
|
+
records: [
|
|
65
|
+
["$it", "$hasDataType", KeyValueRecord],
|
|
66
|
+
...query
|
|
67
|
+
]
|
|
68
|
+
}, async ({ records: records2 }) => {
|
|
69
|
+
const kvRecords = records2;
|
|
70
|
+
const values = await Promise.all(kvRecords.map(toKVRecord));
|
|
71
|
+
if (cancelled) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
setRecords(values);
|
|
75
|
+
setIsLoading(false);
|
|
76
|
+
const ids = new Set(kvRecords.map((r) => r.id));
|
|
77
|
+
recordUnsubscribes.forEach((unsubscribe, id) => {
|
|
78
|
+
if (!ids.has(id)) {
|
|
79
|
+
recordUnsubscribes.delete(id);
|
|
80
|
+
unsubscribe.then((fn) => fn());
|
|
66
81
|
}
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const queryUnsubscribe = lr.Record.subscribeToQuery({
|
|
71
|
-
records: [
|
|
72
|
-
["$it", "$hasDataType", KeyValueRecord],
|
|
73
|
-
...query
|
|
74
|
-
]
|
|
75
|
-
}, async ({ records: records2 }) => {
|
|
76
|
-
const values = await Promise.all(records2.map(async (r) => ({
|
|
77
|
-
_id: r.id,
|
|
78
|
-
_createdAt: r.createdAt,
|
|
79
|
-
...await r.getValue()
|
|
80
|
-
})));
|
|
81
|
-
if (cancelled) {
|
|
82
|
+
});
|
|
83
|
+
kvRecords.forEach((r) => {
|
|
84
|
+
if (recordUnsubscribes.has(r.id)) {
|
|
82
85
|
return;
|
|
83
86
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (subscribedRecordIds.has(r.id)) {
|
|
87
|
+
recordUnsubscribes.set(r.id, r.subscribe(async () => {
|
|
88
|
+
const newValue = await toKVRecord(r);
|
|
89
|
+
if (cancelled) {
|
|
88
90
|
return;
|
|
89
91
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (cancelled) {
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
const newValue = await r.getValue();
|
|
96
|
-
if (cancelled) {
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
setRecords((prev) => prev.map(
|
|
100
|
-
(v) => v._id === r.id ? { _id: r.id, _createdAt: r.createdAt, ...newValue } : v
|
|
101
|
-
));
|
|
102
|
-
});
|
|
103
|
-
});
|
|
92
|
+
setRecords((prev) => prev.map((v) => v._id === r.id ? newValue : v));
|
|
93
|
+
}));
|
|
104
94
|
});
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
95
|
+
});
|
|
96
|
+
queryUnsubscribe.catch((err) => {
|
|
97
|
+
console.error("useKeyValueRecords: could not subscribe to query", err);
|
|
108
98
|
});
|
|
109
99
|
return () => {
|
|
110
100
|
cancelled = true;
|
|
111
|
-
|
|
101
|
+
queryUnsubscribe.then((unsubscribe) => unsubscribe(), () => {
|
|
102
|
+
});
|
|
103
|
+
recordUnsubscribes.forEach((unsubscribe) => unsubscribe.then((fn) => fn()));
|
|
112
104
|
};
|
|
113
105
|
}, [lr, queryKey]);
|
|
114
106
|
return { records, isLoading };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linkedrecords/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"module": "./dist/index.mjs",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"description": "",
|
|
27
27
|
"peerDependencies": {
|
|
28
|
-
"@linkedrecords/browser": "^0.
|
|
28
|
+
"@linkedrecords/browser": "^0.4.0",
|
|
29
29
|
"react": ">=18"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@linkedrecords/browser": "^0.
|
|
32
|
+
"@linkedrecords/browser": "^0.4.0",
|
|
33
33
|
"@types/react": "^19.1.12",
|
|
34
34
|
"react": "^19.1.1",
|
|
35
35
|
"tsup": "^8.5.0",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { type ReactNode, useState } from 'react';
|
|
2
|
+
import { type ReactNode, useMemo, useState } from 'react';
|
|
3
3
|
import LinkedRecords from '@linkedrecords/browser';
|
|
4
4
|
import { LinkedRecordsContext } from './linkedRecordsContext';
|
|
5
5
|
|
|
@@ -47,9 +47,11 @@ export function LinkedRecordsProvider({ children, serverUrl, config }: LinkedRec
|
|
|
47
47
|
const [lr] = useState(() => (config
|
|
48
48
|
? createClient(serverUrl, config)
|
|
49
49
|
: LinkedRecords.getPublicClient(serverUrl)));
|
|
50
|
+
// A new context value would re-render every consumer on each render.
|
|
51
|
+
const value = useMemo(() => ({ lr }), [lr]);
|
|
50
52
|
|
|
51
53
|
return (
|
|
52
|
-
<LinkedRecordsContext.Provider value={
|
|
54
|
+
<LinkedRecordsContext.Provider value={value}>
|
|
53
55
|
{children}
|
|
54
56
|
</LinkedRecordsContext.Provider>
|
|
55
57
|
);
|
package/src/useRecords.ts
CHANGED
|
@@ -1,103 +1,96 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
1
|
import { useEffect, useState } from "react";
|
|
2
|
+
import type LinkedRecords from '@linkedrecords/browser';
|
|
3
|
+
import { KeyValueRecord } from '@linkedrecords/browser';
|
|
3
4
|
import { useLinkedRecords } from "./useLinkedRecords";
|
|
4
5
|
import { getQueryKey } from "./queryKey";
|
|
5
|
-
import { KeyValueRecord } from '@linkedrecords/browser';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
7
|
+
type JSONValue = string | number | boolean | null | JSONValue[] | { [key: string]: JSONValue };
|
|
8
|
+
|
|
9
|
+
type KVValue = { [key: string]: JSONValue | undefined };
|
|
10
10
|
|
|
11
|
-
export
|
|
11
|
+
export type KVRecord<T extends object = KVValue> = T & {
|
|
12
12
|
_id?: string;
|
|
13
13
|
_createdAt?: Date;
|
|
14
|
-
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type CompoundRecordQuery = Parameters<LinkedRecords['Record']['subscribeToQuery']>[0];
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
// One [subject, predicate, object] clause of a record query, e.g. ['$it', 'isA', 'Todo'].
|
|
19
|
+
export type RecordQueryClause = Exclude<CompoundRecordQuery[string], string>[number];
|
|
20
|
+
|
|
21
|
+
export function useKeyValueRecordsWithStatus<T extends object = KVValue>(query: RecordQueryClause[]): { records: KVRecord<T>[], isLoading: boolean } {
|
|
17
22
|
const { lr } = useLinkedRecords();
|
|
18
|
-
const [ records, setRecords ] = useState<KVRecord[]>([]);
|
|
23
|
+
const [ records, setRecords ] = useState<KVRecord<T>[]>([]);
|
|
19
24
|
const [ isLoading, setIsLoading ] = useState(true);
|
|
20
25
|
const queryKey = getQueryKey(query);
|
|
21
26
|
|
|
22
27
|
useEffect(() => {
|
|
23
28
|
setIsLoading(true);
|
|
24
29
|
|
|
25
|
-
//
|
|
26
|
-
//
|
|
30
|
+
// Values are read asynchronously; results which arrive after the query
|
|
31
|
+
// changed or the component unmounted are dropped.
|
|
27
32
|
let cancelled = false;
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
// Record observers by record id, so each record in the result is
|
|
34
|
+
// observed once and records which leave the result are unobserved.
|
|
35
|
+
const recordUnsubscribes = new Map<string | undefined, Promise<() => void>>();
|
|
36
|
+
|
|
37
|
+
// T is the value shape declared by the caller; the hook can not check it.
|
|
38
|
+
const toKVRecord = async (r: KeyValueRecord) => ({
|
|
39
|
+
_id: r.id,
|
|
40
|
+
_createdAt: r.createdAt,
|
|
41
|
+
...(await r.getValue()),
|
|
42
|
+
}) as KVRecord<T>;
|
|
43
|
+
|
|
44
|
+
const queryUnsubscribe = lr.Record.subscribeToQuery({
|
|
45
|
+
records: [
|
|
46
|
+
['$it', '$hasDataType', KeyValueRecord],
|
|
47
|
+
...query
|
|
48
|
+
],
|
|
49
|
+
}, async ({ records }) => {
|
|
50
|
+
const kvRecords = records as KeyValueRecord[];
|
|
51
|
+
const values = await Promise.all(kvRecords.map(toKVRecord));
|
|
52
|
+
|
|
53
|
+
if (cancelled) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
setRecords(values);
|
|
58
|
+
setIsLoading(false);
|
|
59
|
+
|
|
60
|
+
const ids = new Set(kvRecords.map((r) => r.id));
|
|
61
|
+
|
|
62
|
+
recordUnsubscribes.forEach((unsubscribe, id) => {
|
|
63
|
+
if (!ids.has(id)) {
|
|
64
|
+
recordUnsubscribes.delete(id);
|
|
65
|
+
unsubscribe.then((fn) => fn());
|
|
36
66
|
}
|
|
67
|
+
});
|
|
37
68
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
} else {
|
|
41
|
-
setTimeout(checkActorId, 50);
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
checkActorId();
|
|
45
|
-
}).then(() => {
|
|
46
|
-
const queryUnsubscribe = lr.Record.subscribeToQuery({
|
|
47
|
-
records: [
|
|
48
|
-
['$it', '$hasDataType', KeyValueRecord],
|
|
49
|
-
...query
|
|
50
|
-
],
|
|
51
|
-
}, async ({ records }) => {
|
|
52
|
-
const values = await Promise.all(records.map(async (r) => ({
|
|
53
|
-
_id: r.id,
|
|
54
|
-
_createdAt: r.createdAt,
|
|
55
|
-
...(await r.getValue()),
|
|
56
|
-
})));
|
|
57
|
-
|
|
58
|
-
if (cancelled) {
|
|
69
|
+
kvRecords.forEach((r) => {
|
|
70
|
+
if (recordUnsubscribes.has(r.id)) {
|
|
59
71
|
return;
|
|
60
72
|
}
|
|
61
73
|
|
|
62
|
-
|
|
63
|
-
|
|
74
|
+
recordUnsubscribes.set(r.id, r.subscribe(async () => {
|
|
75
|
+
const newValue = await toKVRecord(r);
|
|
64
76
|
|
|
65
|
-
|
|
66
|
-
if (subscribedRecordIds.has(r.id)) {
|
|
77
|
+
if (cancelled) {
|
|
67
78
|
return;
|
|
68
79
|
}
|
|
69
80
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
r.subscribe(async () => {
|
|
73
|
-
if (cancelled) {
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const newValue = await r.getValue();
|
|
78
|
-
|
|
79
|
-
if (cancelled) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Use functional update to avoid stale closure
|
|
84
|
-
setRecords(prev => prev.map(v =>
|
|
85
|
-
v._id === r.id
|
|
86
|
-
? { _id: r.id, _createdAt: r.createdAt, ...newValue }
|
|
87
|
-
: v
|
|
88
|
-
));
|
|
89
|
-
});
|
|
90
|
-
});
|
|
81
|
+
setRecords(prev => prev.map(v => (v._id === r.id ? newValue : v)));
|
|
82
|
+
}));
|
|
91
83
|
});
|
|
84
|
+
});
|
|
92
85
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
};
|
|
86
|
+
queryUnsubscribe.catch((err) => {
|
|
87
|
+
console.error('useKeyValueRecords: could not subscribe to query', err);
|
|
96
88
|
});
|
|
97
89
|
|
|
98
90
|
return () => {
|
|
99
91
|
cancelled = true;
|
|
100
|
-
|
|
92
|
+
queryUnsubscribe.then((unsubscribe) => unsubscribe(), () => {});
|
|
93
|
+
recordUnsubscribes.forEach((unsubscribe) => unsubscribe.then((fn) => fn()));
|
|
101
94
|
}
|
|
102
95
|
// query is represented by queryKey; depending on the array itself would
|
|
103
96
|
// resubscribe on every render.
|
|
@@ -107,8 +100,8 @@ export function useKeyValueRecordsWithStatus(query: any[]): { records: KVRecord[
|
|
|
107
100
|
return { records, isLoading };
|
|
108
101
|
}
|
|
109
102
|
|
|
110
|
-
export function useKeyValueRecords(query:
|
|
111
|
-
return useKeyValueRecordsWithStatus(query).records;
|
|
103
|
+
export function useKeyValueRecords<T extends object = KVValue>(query: RecordQueryClause[]): KVRecord<T>[] {
|
|
104
|
+
return useKeyValueRecordsWithStatus<T>(query).records;
|
|
112
105
|
}
|
|
113
106
|
|
|
114
107
|
/** @deprecated Use useKeyValueRecords instead. */
|