Skip to content

Commit 884d2b6

Browse files
authored
Cleanup depsByKey when all dependant entries are disposed (#498)
1 parent adedc7e commit 884d2b6

File tree

2 files changed

+22
-7
lines changed

2 files changed

+22
-7
lines changed

src/dep.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export type OptimisticDependencyFunction<TKey> =
1717

1818
export type Dep<TKey> = Set<AnyEntry> & {
1919
subscribe: OptimisticWrapOptions<[TKey]>["subscribe"];
20+
cleanup: () => void,
2021
} & Unsubscribable;
2122

2223
export function dep<TKey>(options?: {
@@ -28,9 +29,14 @@ export function dep<TKey>(options?: {
2829
function depend(key: TKey) {
2930
const parent = parentEntrySlot.getValue();
3031
if (parent) {
31-
let dep = depsByKey.get(key);
32+
let dep = depsByKey.get(key) as Dep<TKey>;
3233
if (!dep) {
3334
depsByKey.set(key, dep = new Set as Dep<TKey>);
35+
dep.cleanup = () => {
36+
if (dep.size === 0) {
37+
depsByKey.delete(key);
38+
}
39+
};
3440
}
3541
parent.dependOn(dep);
3642
if (typeof subscribe === "function") {

src/entry.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ export class Entry<TArgs extends any[], TValue> {
111111
// thereby preventing it from being fully garbage collected.
112112
forgetChildren(this);
113113

114+
// Remove this Entry from any sets to which it was added by the
115+
// addToSet method (and do a cleanup).
116+
this.forgetDeps(true);
117+
114118
// Because this entry has been kicked out of the cache (in index.js),
115119
// we've lost the ability to find out if/when this entry becomes dirty,
116120
// whether that happens through a subscription, because of a direct call
@@ -145,9 +149,14 @@ export class Entry<TArgs extends any[], TValue> {
145149
this.deps.add(dep);
146150
}
147151

148-
public forgetDeps() {
152+
public forgetDeps(cleanup?: boolean) {
149153
if (this.deps) {
150-
toArray(this.deps).forEach(dep => dep.delete(this));
154+
toArray(this.deps).forEach(dep => {
155+
dep.delete(this);
156+
if (cleanup) {
157+
dep.cleanup();
158+
}
159+
});
151160
this.deps.clear();
152161
emptySetPool.push(this.deps);
153162
this.deps = null;
@@ -177,6 +186,10 @@ function rememberParent(child: AnyEntry) {
177186
function reallyRecompute(entry: AnyEntry, args: any[]) {
178187
forgetChildren(entry);
179188

189+
// Remove this Entry from any sets to which it was added by the
190+
// addToSet method.
191+
entry.forgetDeps();
192+
180193
// Set entry as the parent entry while calling recomputeNewValue(entry).
181194
parentEntrySlot.withValue(entry, recomputeNewValue, [entry, args]);
182195

@@ -313,10 +326,6 @@ function forgetChildren(parent: AnyEntry) {
313326
});
314327
}
315328

316-
// Remove this parent Entry from any sets to which it was added by the
317-
// addToSet method.
318-
parent.forgetDeps();
319-
320329
// After we forget all our children, this.dirtyChildren must be empty
321330
// and therefore must have been reset to null.
322331
assert(parent.dirtyChildren === null);

0 commit comments

Comments
 (0)