Skip to content

Commit a63f12b

Browse files
authored
Merge pull request #459 from streamich/delete-lifecycle
Propagate `delete` event
2 parents 8c2dcfe + 46e75f8 commit a63f12b

File tree

3 files changed

+52
-1
lines changed

3 files changed

+52
-1
lines changed

src/json-crdt/model/Model.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {AvlMap} from '../../util/trees/avl/AvlMap';
2121
import type {JsonNode, JsonNodeView} from '../nodes/types';
2222
import type {Printable} from '../../util/print/types';
2323
import type {NodeBuilder} from '../../json-crdt-patch';
24+
import type {NodeApi} from './api/nodes';
2425

2526
export const UNDEFINED = new ConNode(ORIGIN, undefined);
2627

@@ -291,6 +292,8 @@ export class Model<N extends JsonNode = JsonNode> implements Printable {
291292
if (isSystemNode) return;
292293
const node = this.index.get(value);
293294
if (!node) return;
295+
const api = node.api;
296+
if (api) (api as NodeApi).events.onDelete();
294297
node.children((child) => this.deleteNodeTree(child.id));
295298
this.index.del(value);
296299
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import {Model} from '../..';
2+
3+
test('does not fire events after node is deleted', () => {
4+
const model = Model.withLogicalClock();
5+
model.api.root({
6+
foo: {
7+
bar: {
8+
baz: 'asdf',
9+
},
10+
},
11+
});
12+
const bar = model.api.obj(['foo', 'bar']);
13+
let cnt = 0;
14+
bar.events.changes.listen(() => {
15+
cnt++;
16+
});
17+
expect(cnt).toBe(0);
18+
bar.set({
19+
gg: 'wp',
20+
});
21+
expect(cnt).toBe(1);
22+
model.api.obj(['foo']).del(['bar']);
23+
model.api.obj(['foo']).set({gl: 'hf'});
24+
expect(cnt).toBe(1);
25+
});

src/json-crdt/model/api/events/NodeEvents.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@ export interface NodeEventMap {
2121
}
2222

2323
class ChangesFanOut<N extends JsonNode = JsonNode> extends FanOut<JsonNodeView<N>> {
24+
/** @ignore */
2425
private _v: JsonNodeView<N> | undefined = undefined;
26+
/** @ignore */
2527
private _u: FanOutUnsubscribe | undefined = undefined;
2628

29+
/** @ignore */
2730
constructor(private readonly api: NodeApi<N>) {
2831
super();
2932
}
@@ -45,10 +48,20 @@ class ChangesFanOut<N extends JsonNode = JsonNode> extends FanOut<JsonNodeView<N
4548
unsubscribe();
4649
if (!this.listeners.size) {
4750
this._u?.();
48-
// this._unsub = this._view = undefined;
51+
this._u = this._v = undefined;
4952
}
5053
};
5154
}
55+
56+
/**
57+
* @internal
58+
* @ignore
59+
*/
60+
public dispose() {
61+
this.listeners.clear();
62+
this._u?.();
63+
this._u = this._v = undefined;
64+
}
5265
}
5366

5467
export class NodeEvents<N extends JsonNode = JsonNode>
@@ -101,6 +114,16 @@ export class NodeEvents<N extends JsonNode = JsonNode>
101114
super.off(type, listener, options);
102115
}
103116

117+
/**
118+
* Called when this node is deleted.
119+
*
120+
* @internal
121+
* @ignore
122+
*/
123+
public onDelete() {
124+
this.changes.dispose();
125+
}
126+
104127
// ---------------------------------------------------------------- SyncStore
105128

106129
public readonly subscribe = (callback: () => void): SyncStoreUnsubscribe => {

0 commit comments

Comments
 (0)