Skip to content

Commit 7682442

Browse files
committed
Enhance subscription management by adding root subscription source functions and updating regSub to handle string-based subscriptions. Implement validation to prevent duplicate root-key registration. Update tests to cover new functionality and error handling.
1 parent f50f6b1 commit 7682442

4 files changed

Lines changed: 78 additions & 13 deletions

File tree

src/db.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Db } from './types';
2-
import { getReaction } from './registrar';
2+
import { getReaction, getRootSubIdBySource } from './registrar';
33
import { consoleLog } from './loggers';
44
import { scheduleAfterRender } from './schedule';
55

@@ -30,7 +30,16 @@ export function updateAppDbWithPatches<T = Record<string, any>>(newDb: Db<T>, pa
3030
const pathSegments = patch.path;
3131
if (pathSegments.length > 0) {
3232
const rootKey = pathSegments[0] as string;
33-
const subVectorKey = JSON.stringify([rootKey])
33+
if (typeof rootKey !== 'string') {
34+
continue;
35+
}
36+
37+
const subId = getRootSubIdBySource(rootKey);
38+
if (!subId) {
39+
continue;
40+
}
41+
42+
const subVectorKey = JSON.stringify([subId])
3443
const reaction = getReaction(subVectorKey);
3544
if (reaction) {
3645
if (!reaction.isRoot) {
@@ -66,4 +75,4 @@ export function updateAppDbWithPatches<T = Record<string, any>>(newDb: Db<T>, pa
6675
});
6776
}
6877
}
69-
}
78+
}

src/registrar.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,16 @@ export function clearHandlers(kind?: Kind, id?: string): void {
4242
for (const k in kindToIdToHandler) {
4343
kindToIdToHandler[k as Kind] = {};
4444
}
45+
clearRootSubSources();
4546
} else if (id == null) {
4647
if (!(kind in kindToIdToHandler)) {
4748
consoleLog('error', `[reflex] Unknown kind: ${kind}`);
4849
return;
4950
}
5051
kindToIdToHandler[kind] = {};
52+
if (kind === 'sub') {
53+
clearRootSubSources();
54+
}
5155
} else {
5256
if (kindToIdToHandler[kind][id]) {
5357
delete kindToIdToHandler[kind][id];
@@ -90,6 +94,21 @@ export function clearReactions(id?: string): void {
9094
}
9195
}
9296

97+
// === Root Subscription Source Registry Functions ===
98+
const rootSubIdBySource = new Map<string, Id>();
99+
100+
export function setRootSubSource(subId: Id, sourceKey: string): void {
101+
rootSubIdBySource.set(sourceKey, subId);
102+
}
103+
104+
export function getRootSubIdBySource(sourceKey: string): Id | undefined {
105+
return rootSubIdBySource.get(sourceKey);
106+
}
107+
108+
export function clearRootSubSources(): void {
109+
rootSubIdBySource.clear();
110+
}
111+
93112
export function clearSubs(): void {
94113
clearReactions();
95114
clearHandlers('sub');
@@ -152,4 +171,4 @@ export function clearAllRegistries(): void {
152171
clearReactions();
153172
clearInterceptors();
154173
clearSubConfigs();
155-
}
174+
}

src/subs.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import {
88
registerHandler,
99
hasHandler,
1010
setSubConfig,
11-
getSubConfig
11+
getSubConfig,
12+
setRootSubSource,
13+
getRootSubIdBySource
1214
} from './registrar';
1315
import { getAppDb } from './db';
1416
import { mergeTrace, withTrace } from './trace';
@@ -17,18 +19,26 @@ import { getGlobalEqualityCheck } from './settings';
1719
const KIND = 'sub';
1820
const KIND_DEPS = 'subDeps';
1921

22+
function registerRootSub (id: Id, sourceKey: string) {
23+
const conflictingSubId = getRootSubIdBySource(sourceKey)
24+
if (conflictingSubId && conflictingSubId !== id) {
25+
consoleLog('error', `[reflex] Subscription with id '${id}' will be overridden. Root key '${sourceKey}' is already used by subscription '${conflictingSubId}'.`)
26+
}
27+
28+
setRootSubSource(id, sourceKey)
29+
registerHandler(KIND, id, () => getAppDb()[sourceKey])
30+
registerHandler(KIND_DEPS, id, () => [])
31+
}
32+
2033
export function regSub<R>(id: Id, computeFn?: ((...values: any[]) => R) | string, depsFn?: (...params: any[]) => SubVector[], config?: SubConfig): void {
2134
if (hasHandler(KIND, id)) {
2235
consoleLog('warn', `[reflex] Overriding. Subscription '${id}' already registered.`)
2336
}
24-
// If only id is provided, use root subscription logic
37+
2538
if (!computeFn) {
26-
registerHandler(KIND, id, () => getAppDb()[id])
27-
registerHandler(KIND_DEPS, id, () => [])
39+
registerRootSub(id, id)
2840
} else if (typeof computeFn === 'string') {
29-
// String field subscription - access field directly from appdb
30-
registerHandler(KIND, id, () => getAppDb()[computeFn])
31-
registerHandler(KIND_DEPS, id, () => [])
41+
registerRootSub(id, computeFn as string)
3242
} else {
3343
// Computed subscriptions require depsFn
3444
if (!depsFn) {
@@ -100,4 +110,4 @@ export function getOrCreateReaction(subVector: SubVector): Reaction<any> {
100110
export function getSubscriptionValue<T>(subVector: SubVector): T {
101111
const reaction = getOrCreateReaction(subVector)
102112
return reaction ? reaction.computeValue() : undefined as T
103-
}
113+
}

src/tests/hook.test.tsx

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,33 @@ describe('React Hooks', () => {
7171
expect(result.current).toBe('john@example.com');
7272
});
7373

74+
it('should update string-based root subscription when source field changes', async () => {
75+
const { result } = renderHook(() => useSubscription(['user-email-str']));
76+
77+
expect(result.current).toBe('john@example.com');
78+
79+
regEvent('set-user-email', ({ draftDb }, email) => {
80+
draftDb.userEmail = email;
81+
});
82+
83+
act(() => {
84+
dispatch(['set-user-email', 'jane@example.com']);
85+
});
86+
87+
await waitFor(() => {
88+
expect(result.current).toBe('jane@example.com');
89+
});
90+
});
91+
92+
it('should reject duplicate root-key registration with different sub ids', () => {
93+
regSub('user-email-str-duplicate', 'userEmail');
94+
95+
expectLogCall(
96+
'error',
97+
"[reflex] Subscription with id 'user-email-str-duplicate' will be overridden. Root key 'userEmail' is already used by subscription 'user-email-str'."
98+
);
99+
});
100+
74101
it('should handle subscription with parameters', () => {
75102
// Register a parameterized subscription
76103
regSub('todo-by-id', (todos, id) => {
@@ -273,4 +300,4 @@ describe('React Hooks', () => {
273300
});
274301
});
275302
});
276-
});
303+
});

0 commit comments

Comments
 (0)