@dxos/echo-solid 0.0.0 → 0.8.4-main.1c7ec43d41
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/lib/neutral/index.mjs +210 -0
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/useObject.d.ts +101 -0
- package/dist/types/src/useObject.d.ts.map +1 -0
- package/dist/types/src/useObject.test.d.ts +2 -0
- package/dist/types/src/useObject.test.d.ts.map +1 -0
- package/dist/types/src/useQuery.d.ts +14 -0
- package/dist/types/src/useQuery.d.ts.map +1 -0
- package/dist/types/src/useQuery.test.d.ts +2 -0
- package/dist/types/src/useQuery.test.d.ts.map +1 -0
- package/dist/types/src/useSchema.d.ts +14 -0
- package/dist/types/src/useSchema.d.ts.map +1 -0
- package/dist/types/src/useSchema.test.d.ts +2 -0
- package/dist/types/src/useSchema.test.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +15 -14
- package/src/index.ts +0 -1
- package/src/useObject.test.tsx +12 -12
- package/src/useObject.ts +222 -76
- package/src/useQuery.test.tsx +18 -18
- package/src/useSchema.test.tsx +2 -2
- package/src/useSchema.ts +1 -1
- package/src/useRef.test.tsx +0 -247
- package/src/useRef.ts +0 -48
package/src/useQuery.test.tsx
CHANGED
|
@@ -6,9 +6,9 @@ import { render, waitFor } from '@solidjs/testing-library';
|
|
|
6
6
|
import { type JSX, createSignal } from 'solid-js';
|
|
7
7
|
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
|
|
8
8
|
|
|
9
|
-
import { Filter, Obj, Query
|
|
10
|
-
import { TestSchema } from '@dxos/echo/testing';
|
|
9
|
+
import { Filter, Obj, Query } from '@dxos/echo';
|
|
11
10
|
import { EchoTestBuilder } from '@dxos/echo-db/testing';
|
|
11
|
+
import { TestSchema } from '@dxos/echo/testing';
|
|
12
12
|
|
|
13
13
|
import { useQuery } from './useQuery';
|
|
14
14
|
|
|
@@ -30,7 +30,7 @@ describe('useQuery', () => {
|
|
|
30
30
|
let result: any[] | undefined;
|
|
31
31
|
|
|
32
32
|
render(() => {
|
|
33
|
-
const objects = useQuery(undefined, Filter.type(
|
|
33
|
+
const objects = useQuery(undefined, Filter.type(TestSchema.Expando));
|
|
34
34
|
result = objects();
|
|
35
35
|
return (<div>test</div>) as JSX.Element;
|
|
36
36
|
});
|
|
@@ -42,7 +42,7 @@ describe('useQuery', () => {
|
|
|
42
42
|
let result: any[] | undefined;
|
|
43
43
|
|
|
44
44
|
render(() => {
|
|
45
|
-
const objects = useQuery(db, Filter.type(
|
|
45
|
+
const objects = useQuery(db, Filter.type(TestSchema.Expando));
|
|
46
46
|
result = objects();
|
|
47
47
|
return (<div>test</div>) as JSX.Element;
|
|
48
48
|
});
|
|
@@ -54,8 +54,8 @@ describe('useQuery', () => {
|
|
|
54
54
|
|
|
55
55
|
test('returns matching objects', async () => {
|
|
56
56
|
// Add some objects to the database
|
|
57
|
-
const obj1 = Obj.make(
|
|
58
|
-
const obj2 = Obj.make(
|
|
57
|
+
const obj1 = Obj.make(TestSchema.Expando, { name: 'Alice' });
|
|
58
|
+
const obj2 = Obj.make(TestSchema.Expando, { name: 'Bob' });
|
|
59
59
|
db.add(obj1);
|
|
60
60
|
db.add(obj2);
|
|
61
61
|
await db.flush({ indexes: true });
|
|
@@ -63,7 +63,7 @@ describe('useQuery', () => {
|
|
|
63
63
|
let objectsAccessor: (() => any[]) | undefined;
|
|
64
64
|
|
|
65
65
|
function TestComponent() {
|
|
66
|
-
const objects = useQuery(db, Filter.type(
|
|
66
|
+
const objects = useQuery(db, Filter.type(TestSchema.Expando));
|
|
67
67
|
objectsAccessor = objects;
|
|
68
68
|
return (<div data-testid='count'>{objects().length}</div>) as JSX.Element;
|
|
69
69
|
}
|
|
@@ -84,7 +84,7 @@ describe('useQuery', () => {
|
|
|
84
84
|
let objectsAccessor: (() => any[]) | undefined;
|
|
85
85
|
|
|
86
86
|
function TestComponent() {
|
|
87
|
-
const objects = useQuery(db, Filter.type(
|
|
87
|
+
const objects = useQuery(db, Filter.type(TestSchema.Expando));
|
|
88
88
|
objectsAccessor = objects;
|
|
89
89
|
return (<div data-testid='count'>{objects().length}</div>) as JSX.Element;
|
|
90
90
|
}
|
|
@@ -96,7 +96,7 @@ describe('useQuery', () => {
|
|
|
96
96
|
});
|
|
97
97
|
|
|
98
98
|
// Add an object
|
|
99
|
-
const obj = Obj.make(
|
|
99
|
+
const obj = Obj.make(TestSchema.Expando, { name: 'Charlie' });
|
|
100
100
|
db.add(obj);
|
|
101
101
|
await db.flush({ indexes: true });
|
|
102
102
|
|
|
@@ -112,8 +112,8 @@ describe('useQuery', () => {
|
|
|
112
112
|
|
|
113
113
|
test('updates when objects are removed', async () => {
|
|
114
114
|
// Add objects first
|
|
115
|
-
const obj1 = Obj.make(
|
|
116
|
-
const obj2 = Obj.make(
|
|
115
|
+
const obj1 = Obj.make(TestSchema.Expando, { name: 'Alice' });
|
|
116
|
+
const obj2 = Obj.make(TestSchema.Expando, { name: 'Bob' });
|
|
117
117
|
db.add(obj1);
|
|
118
118
|
db.add(obj2);
|
|
119
119
|
await db.flush({ indexes: true });
|
|
@@ -121,7 +121,7 @@ describe('useQuery', () => {
|
|
|
121
121
|
let objectsAccessor: (() => any[]) | undefined;
|
|
122
122
|
|
|
123
123
|
function TestComponent() {
|
|
124
|
-
const objects = useQuery(db, Filter.type(
|
|
124
|
+
const objects = useQuery(db, Filter.type(TestSchema.Expando));
|
|
125
125
|
objectsAccessor = objects;
|
|
126
126
|
return (<div data-testid='count'>{objects().length}</div>) as JSX.Element;
|
|
127
127
|
}
|
|
@@ -175,14 +175,14 @@ describe('useQuery', () => {
|
|
|
175
175
|
});
|
|
176
176
|
|
|
177
177
|
test('accepts Query directly', async () => {
|
|
178
|
-
const obj = Obj.make(
|
|
178
|
+
const obj = Obj.make(TestSchema.Expando, { name: 'Test' });
|
|
179
179
|
db.add(obj);
|
|
180
180
|
await db.flush({ indexes: true });
|
|
181
181
|
|
|
182
182
|
let objectsAccessor: (() => any[]) | undefined;
|
|
183
183
|
|
|
184
184
|
function TestComponent() {
|
|
185
|
-
const objects = useQuery(db, Query.select(Filter.type(
|
|
185
|
+
const objects = useQuery(db, Query.select(Filter.type(TestSchema.Expando)));
|
|
186
186
|
objectsAccessor = objects;
|
|
187
187
|
return (<div data-testid='count'>{objects().length}</div>) as JSX.Element;
|
|
188
188
|
}
|
|
@@ -200,7 +200,7 @@ describe('useQuery', () => {
|
|
|
200
200
|
});
|
|
201
201
|
|
|
202
202
|
test('accepts reactive database accessor', async () => {
|
|
203
|
-
const obj = Obj.make(
|
|
203
|
+
const obj = Obj.make(TestSchema.Expando, { name: 'Test' });
|
|
204
204
|
db.add(obj);
|
|
205
205
|
await db.flush({ indexes: true });
|
|
206
206
|
|
|
@@ -208,7 +208,7 @@ describe('useQuery', () => {
|
|
|
208
208
|
let dbAccessor: any = db;
|
|
209
209
|
|
|
210
210
|
function TestComponent() {
|
|
211
|
-
const objects = useQuery(() => dbAccessor, Filter.type(
|
|
211
|
+
const objects = useQuery(() => dbAccessor, Filter.type(TestSchema.Expando));
|
|
212
212
|
objectsAccessor = objects;
|
|
213
213
|
return (<div data-testid='count'>{objects().length}</div>) as JSX.Element;
|
|
214
214
|
}
|
|
@@ -235,7 +235,7 @@ describe('useQuery', () => {
|
|
|
235
235
|
// Register schema first
|
|
236
236
|
await db.graph.schemaRegistry.register([TestSchema.Person]);
|
|
237
237
|
|
|
238
|
-
const obj1 = Obj.make(
|
|
238
|
+
const obj1 = Obj.make(TestSchema.Expando, { name: 'Test1' });
|
|
239
239
|
const obj2 = Obj.make(TestSchema.Person, { name: 'Test2', username: 'test', email: 'test@example.com' });
|
|
240
240
|
db.add(obj1);
|
|
241
241
|
db.add(obj2);
|
|
@@ -246,7 +246,7 @@ describe('useQuery', () => {
|
|
|
246
246
|
|
|
247
247
|
function TestComponent() {
|
|
248
248
|
const objects = useQuery(db, () =>
|
|
249
|
-
usePersonFilter() ? Filter.type(TestSchema.Person) : Filter.type(
|
|
249
|
+
usePersonFilter() ? Filter.type(TestSchema.Person) : Filter.type(TestSchema.Expando),
|
|
250
250
|
);
|
|
251
251
|
objectsAccessor = objects;
|
|
252
252
|
return (<div data-testid='count'>{objects().length}</div>) as JSX.Element;
|
package/src/useSchema.test.tsx
CHANGED
|
@@ -7,8 +7,8 @@ import { type JSX, createMemo } from 'solid-js';
|
|
|
7
7
|
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
|
|
8
8
|
|
|
9
9
|
import { Type } from '@dxos/echo';
|
|
10
|
-
import { TestSchema } from '@dxos/echo/testing';
|
|
11
10
|
import { EchoTestBuilder } from '@dxos/echo-db/testing';
|
|
11
|
+
import { TestSchema } from '@dxos/echo/testing';
|
|
12
12
|
|
|
13
13
|
import { useSchema } from './useSchema';
|
|
14
14
|
|
|
@@ -100,7 +100,7 @@ describe('useSchema', () => {
|
|
|
100
100
|
// The runtime registry query subscription doesn't fire when schemas are registered.
|
|
101
101
|
// See: packages/core/echo/echo-db/src/proxy-db/runtime-schema-registry.ts:57
|
|
102
102
|
let schemaAccessor: (() => any) | undefined;
|
|
103
|
-
const typename = 'example.
|
|
103
|
+
const typename = 'com.example.type.person';
|
|
104
104
|
|
|
105
105
|
function TestComponent() {
|
|
106
106
|
const schema = useSchema(db, typename);
|
package/src/useSchema.ts
CHANGED
|
@@ -16,7 +16,7 @@ type MaybeAccessor<T> = T | Accessor<T>;
|
|
|
16
16
|
* @param typename - The schema typename to query (can be reactive)
|
|
17
17
|
* @returns An accessor that returns the current schema or undefined
|
|
18
18
|
*/
|
|
19
|
-
export const useSchema = <T extends Type.
|
|
19
|
+
export const useSchema = <T extends Type.AnyEntity = Type.AnyEntity>(
|
|
20
20
|
db?: MaybeAccessor<Database.Database | undefined>,
|
|
21
21
|
typename?: MaybeAccessor<string | undefined>,
|
|
22
22
|
): Accessor<T | undefined> => {
|
package/src/useRef.test.tsx
DELETED
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { render, waitFor } from '@solidjs/testing-library';
|
|
6
|
-
import { type JSX, createSignal } from 'solid-js';
|
|
7
|
-
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
|
|
8
|
-
|
|
9
|
-
import { Obj, Ref, Type } from '@dxos/echo';
|
|
10
|
-
import { TestSchema } from '@dxos/echo/testing';
|
|
11
|
-
import { EchoTestBuilder } from '@dxos/echo-db/testing';
|
|
12
|
-
import { Registry, RegistryProvider } from '@dxos/effect-atom-solid';
|
|
13
|
-
|
|
14
|
-
import { useRef } from './useRef';
|
|
15
|
-
|
|
16
|
-
const createWrapper = (registry: Registry.Registry) => {
|
|
17
|
-
return (props: { children: JSX.Element }) => {
|
|
18
|
-
return <RegistryProvider registry={registry}>{props.children}</RegistryProvider>;
|
|
19
|
-
};
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
describe('useRef', () => {
|
|
23
|
-
let testBuilder: EchoTestBuilder;
|
|
24
|
-
let db: any;
|
|
25
|
-
let registry: Registry.Registry;
|
|
26
|
-
|
|
27
|
-
beforeEach(async () => {
|
|
28
|
-
testBuilder = await new EchoTestBuilder().open();
|
|
29
|
-
const { db: database } = await testBuilder.createDatabase();
|
|
30
|
-
db = database;
|
|
31
|
-
registry = Registry.make();
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
afterEach(async () => {
|
|
35
|
-
await testBuilder.close();
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test('returns undefined when ref is undefined', () => {
|
|
39
|
-
const Wrapper = createWrapper(registry);
|
|
40
|
-
|
|
41
|
-
let result: any;
|
|
42
|
-
|
|
43
|
-
render(
|
|
44
|
-
() => {
|
|
45
|
-
const target = useRef(undefined);
|
|
46
|
-
result = target();
|
|
47
|
-
return (<div>test</div>) as JSX.Element;
|
|
48
|
-
},
|
|
49
|
-
{ wrapper: Wrapper },
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
expect(result).toBeUndefined();
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test('returns undefined when ref target is not loaded and loads it', async () => {
|
|
56
|
-
await db.graph.schemaRegistry.register([TestSchema.Person]);
|
|
57
|
-
|
|
58
|
-
// Create objects with a ref
|
|
59
|
-
const targetObj = Obj.make(TestSchema.Person, { name: 'Target', username: 'target', email: 'target@example.com' });
|
|
60
|
-
db.add(targetObj);
|
|
61
|
-
await db.flush({ indexes: true });
|
|
62
|
-
|
|
63
|
-
// Create a ref - target should be available since object is in memory
|
|
64
|
-
const ref = Ref.make(targetObj);
|
|
65
|
-
|
|
66
|
-
// Initially target should be available since object is in memory
|
|
67
|
-
expect(ref.target).toBeDefined();
|
|
68
|
-
|
|
69
|
-
const Wrapper = createWrapper(registry);
|
|
70
|
-
|
|
71
|
-
let targetAccessor: (() => any) | undefined;
|
|
72
|
-
|
|
73
|
-
function TestComponent() {
|
|
74
|
-
const target = useRef(ref);
|
|
75
|
-
targetAccessor = target;
|
|
76
|
-
return (<div data-testid='name'>{target()?.name || 'undefined'}</div>) as JSX.Element;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const { getByTestId } = render(() => <TestComponent />, { wrapper: Wrapper });
|
|
80
|
-
|
|
81
|
-
// Should load since target is available
|
|
82
|
-
await waitFor(() => {
|
|
83
|
-
expect(getByTestId('name').textContent).toBe('Target');
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// Get the actual result from the accessor
|
|
87
|
-
const result = targetAccessor?.();
|
|
88
|
-
expect(result?.name).toBe('Target');
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test('returns target when ref is loaded', async () => {
|
|
92
|
-
await db.graph.schemaRegistry.register([TestSchema.Person]);
|
|
93
|
-
|
|
94
|
-
const targetObj = Obj.make(TestSchema.Person, { name: 'Target', username: 'target', email: 'target@example.com' });
|
|
95
|
-
db.add(targetObj);
|
|
96
|
-
await db.flush({ indexes: true });
|
|
97
|
-
|
|
98
|
-
const ref = Ref.make(targetObj);
|
|
99
|
-
const Wrapper = createWrapper(registry);
|
|
100
|
-
|
|
101
|
-
let targetAccessor: (() => any) | undefined;
|
|
102
|
-
|
|
103
|
-
function TestComponent() {
|
|
104
|
-
const target = useRef(ref);
|
|
105
|
-
targetAccessor = target;
|
|
106
|
-
return (<div data-testid='name'>{target()?.name || 'undefined'}</div>) as JSX.Element;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const { getByTestId } = render(() => <TestComponent />, { wrapper: Wrapper });
|
|
110
|
-
|
|
111
|
-
await waitFor(() => {
|
|
112
|
-
expect(getByTestId('name').textContent).toBe('Target');
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
// Get the actual result from the accessor
|
|
116
|
-
const result = targetAccessor?.();
|
|
117
|
-
expect(result?.name).toBe('Target');
|
|
118
|
-
expect(result?.username).toBe('target');
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
test('handles ref to Expando object', async () => {
|
|
122
|
-
const targetObj = Obj.make(Type.Expando, { name: 'Expando Target', value: 42 });
|
|
123
|
-
db.add(targetObj);
|
|
124
|
-
await db.flush({ indexes: true });
|
|
125
|
-
|
|
126
|
-
const ref = Ref.make(targetObj);
|
|
127
|
-
const Wrapper = createWrapper(registry);
|
|
128
|
-
|
|
129
|
-
let targetAccessor: (() => any) | undefined;
|
|
130
|
-
|
|
131
|
-
function TestComponent() {
|
|
132
|
-
const target = useRef(ref);
|
|
133
|
-
targetAccessor = target;
|
|
134
|
-
return (<div data-testid='name'>{target()?.name || 'undefined'}</div>) as JSX.Element;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const { getByTestId } = render(() => <TestComponent />, { wrapper: Wrapper });
|
|
138
|
-
|
|
139
|
-
await waitFor(() => {
|
|
140
|
-
expect(getByTestId('name').textContent).toBe('Expando Target');
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// Get the actual result from the accessor
|
|
144
|
-
const result = targetAccessor?.();
|
|
145
|
-
expect(result?.name).toBe('Expando Target');
|
|
146
|
-
expect(result?.value).toBe(42);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
test('works with accessor function', async () => {
|
|
150
|
-
await db.graph.schemaRegistry.register([TestSchema.Person]);
|
|
151
|
-
|
|
152
|
-
const targetObj = Obj.make(TestSchema.Person, { name: 'Target', username: 'target', email: 'target@example.com' });
|
|
153
|
-
db.add(targetObj);
|
|
154
|
-
await db.flush({ indexes: true });
|
|
155
|
-
|
|
156
|
-
const ref = Ref.make(targetObj);
|
|
157
|
-
const Wrapper = createWrapper(registry);
|
|
158
|
-
|
|
159
|
-
let targetAccessor: (() => any) | undefined;
|
|
160
|
-
|
|
161
|
-
function TestComponent() {
|
|
162
|
-
const target = useRef(() => ref);
|
|
163
|
-
targetAccessor = target;
|
|
164
|
-
return (<div data-testid='name'>{target()?.name || 'undefined'}</div>) as JSX.Element;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const { getByTestId } = render(() => <TestComponent />, { wrapper: Wrapper });
|
|
168
|
-
|
|
169
|
-
await waitFor(() => {
|
|
170
|
-
expect(getByTestId('name').textContent).toBe('Target');
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// Get the actual result from the accessor
|
|
174
|
-
const result = targetAccessor?.();
|
|
175
|
-
expect(result?.name).toBe('Target');
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
test('reactively tracks changes when accessor returns different ref', async () => {
|
|
179
|
-
await db.graph.schemaRegistry.register([TestSchema.Person]);
|
|
180
|
-
|
|
181
|
-
const targetObj1 = Obj.make(TestSchema.Person, {
|
|
182
|
-
name: 'Target1',
|
|
183
|
-
username: 'target1',
|
|
184
|
-
email: 'target1@example.com',
|
|
185
|
-
});
|
|
186
|
-
const targetObj2 = Obj.make(TestSchema.Person, {
|
|
187
|
-
name: 'Target2',
|
|
188
|
-
username: 'target2',
|
|
189
|
-
email: 'target2@example.com',
|
|
190
|
-
});
|
|
191
|
-
db.add(targetObj1);
|
|
192
|
-
db.add(targetObj2);
|
|
193
|
-
await db.flush({ indexes: true });
|
|
194
|
-
|
|
195
|
-
const ref1 = Ref.make(targetObj1);
|
|
196
|
-
const ref2 = Ref.make(targetObj2);
|
|
197
|
-
const [refSignal, setRefSignal] = createSignal<Ref.Ref<any> | undefined>(ref1);
|
|
198
|
-
const Wrapper = createWrapper(registry);
|
|
199
|
-
|
|
200
|
-
let targetAccessor: (() => any) | undefined;
|
|
201
|
-
|
|
202
|
-
function TestComponent() {
|
|
203
|
-
const target = useRef(refSignal);
|
|
204
|
-
targetAccessor = target;
|
|
205
|
-
return (<div data-testid='name'>{target()?.name || 'undefined'}</div>) as JSX.Element;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const { getByTestId } = render(() => <TestComponent />, { wrapper: Wrapper });
|
|
209
|
-
|
|
210
|
-
await waitFor(() => {
|
|
211
|
-
expect(getByTestId('name').textContent).toBe('Target1');
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
// Get the actual result from the accessor
|
|
215
|
-
let result = targetAccessor?.();
|
|
216
|
-
expect(result?.name).toBe('Target1');
|
|
217
|
-
|
|
218
|
-
// Change the ref via signal
|
|
219
|
-
setRefSignal(() => ref2);
|
|
220
|
-
|
|
221
|
-
await waitFor(() => {
|
|
222
|
-
expect(getByTestId('name').textContent).toBe('Target2');
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
// Get the actual result from the accessor
|
|
226
|
-
result = targetAccessor?.();
|
|
227
|
-
expect(result?.name).toBe('Target2');
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
test('handles undefined when accessor returns undefined', () => {
|
|
231
|
-
const [refSignal] = createSignal<Ref.Ref<any> | undefined>(undefined);
|
|
232
|
-
const Wrapper = createWrapper(registry);
|
|
233
|
-
|
|
234
|
-
let targetAccessor: (() => any) | undefined;
|
|
235
|
-
|
|
236
|
-
function TestComponent() {
|
|
237
|
-
const target = useRef(refSignal);
|
|
238
|
-
targetAccessor = target;
|
|
239
|
-
return (<div data-testid='name'>{target()?.name || 'undefined'}</div>) as JSX.Element;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const { getByTestId } = render(() => <TestComponent />, { wrapper: Wrapper });
|
|
243
|
-
|
|
244
|
-
expect(getByTestId('name').textContent).toBe('undefined');
|
|
245
|
-
expect(targetAccessor?.()).toBeUndefined();
|
|
246
|
-
});
|
|
247
|
-
});
|
package/src/useRef.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { type MaybeAccessor, access } from '@solid-primitives/utils';
|
|
6
|
-
import { type Accessor, createEffect, createMemo, createSignal, onCleanup } from 'solid-js';
|
|
7
|
-
|
|
8
|
-
import { type Entity, type Ref } from '@dxos/echo';
|
|
9
|
-
import { AtomRef } from '@dxos/echo-atom';
|
|
10
|
-
import { useRegistry } from '@dxos/effect-atom-solid';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Subscribe to a reference target object.
|
|
14
|
-
* Returns undefined if the reference hasn't loaded yet, and automatically updates when the target loads or changes.
|
|
15
|
-
*
|
|
16
|
-
* @param ref - The reference to subscribe to (can be reactive).
|
|
17
|
-
* @returns An accessor that returns the current target object or undefined if not loaded.
|
|
18
|
-
*/
|
|
19
|
-
export function useRef<T extends Entity.Unknown>(ref: MaybeAccessor<Ref.Ref<T> | undefined>): Accessor<T | undefined> {
|
|
20
|
-
const registry = useRegistry();
|
|
21
|
-
|
|
22
|
-
// Memoize the ref to track changes.
|
|
23
|
-
const memoizedRef = createMemo(() => access(ref));
|
|
24
|
-
|
|
25
|
-
// Store the current target in a signal.
|
|
26
|
-
const [target, setTarget] = createSignal<T | undefined>(undefined);
|
|
27
|
-
|
|
28
|
-
// Subscribe to ref target changes.
|
|
29
|
-
createEffect(() => {
|
|
30
|
-
const currentRef = memoizedRef();
|
|
31
|
-
|
|
32
|
-
const atom = AtomRef.make(currentRef);
|
|
33
|
-
const currentValue = registry.get(atom);
|
|
34
|
-
setTarget(() => currentValue);
|
|
35
|
-
|
|
36
|
-
const unsubscribe = registry.subscribe(
|
|
37
|
-
atom,
|
|
38
|
-
() => {
|
|
39
|
-
setTarget(() => registry.get(atom));
|
|
40
|
-
},
|
|
41
|
-
{ immediate: true },
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
onCleanup(unsubscribe);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
return target;
|
|
48
|
-
}
|