Skip to content

Commit 13c6946

Browse files
committed
refactor: simplify for size
1 parent 91f2954 commit 13c6946

File tree

2 files changed

+30
-34
lines changed

2 files changed

+30
-34
lines changed

packages/reactivity/__tests__/effectScope.spec.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ describe('reactivity/effect/scope', () => {
7878
})
7979

8080
expect(scope.effects.length).toBe(1)
81-
expect(scope.scopes.length).toBe(1)
82-
expect(scope.scopes[0]).toBeInstanceOf(EffectScope)
81+
expect(scope.scopes!.length).toBe(1)
82+
expect(scope.scopes![0]).toBeInstanceOf(EffectScope)
8383

8484
expect(dummy).toBe(0)
8585
counter.num = 7
@@ -195,9 +195,9 @@ describe('reactivity/effect/scope', () => {
195195
it('should derefence child scope from parent scope after stopping child scope (no memleaks)', async () => {
196196
const parent = new EffectScope()
197197
const child = parent.run(() => new EffectScope())!
198-
expect(parent.scopes.includes(child)).toBe(true)
198+
expect(parent.scopes!.includes(child)).toBe(true)
199199
child.stop()
200-
expect(parent.scopes.includes(child)).toBe(false)
200+
expect(parent.scopes!.includes(child)).toBe(false)
201201
})
202202

203203
it('test with higher level APIs', async () => {

packages/reactivity/src/effectScope.ts

+26-30
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,25 @@ export class EffectScope {
88
active = true
99
effects: ReactiveEffect[] = []
1010
cleanups: (() => void)[] = []
11+
1112
parent: EffectScope | undefined
12-
private children: EffectScope[] | undefined
13-
private parentIndex: number | undefined
13+
scopes: EffectScope[] | undefined
14+
/**
15+
* track a child scope's index in its parent's scopes array for optimized
16+
* removal
17+
*/
18+
private index: number | undefined
1419

1520
constructor(detached = false) {
16-
if (!detached) {
17-
this.recordEffectScope()
21+
if (!detached && activeEffectScope) {
22+
this.parent = activeEffectScope
23+
this.index =
24+
(activeEffectScope.scopes || (activeEffectScope.scopes = [])).push(
25+
this
26+
) - 1
1827
}
1928
}
2029

21-
get scopes(): EffectScope[] {
22-
if (!this.children) this.children = []
23-
return this.children
24-
}
25-
2630
run<T>(fn: () => T): T | undefined {
2731
if (this.active) {
2832
try {
@@ -50,33 +54,25 @@ export class EffectScope {
5054
}
5155
}
5256

53-
stop() {
57+
stop(fromParent?: boolean) {
5458
if (this.active) {
5559
this.effects.forEach(e => e.stop())
56-
this.children?.forEach(e => e.stop())
5760
this.cleanups.forEach(cleanup => cleanup())
58-
this.parent?.derefChildScope(this)
61+
if (this.scopes) {
62+
this.scopes.forEach(e => e.stop(true))
63+
}
64+
// nested scope, dereference from parent to avoid memory leaks
65+
if (this.parent && !fromParent) {
66+
// optimized O(1) removal
67+
const last = this.parent.scopes!.pop()
68+
if (last && last !== this) {
69+
this.parent.scopes![this.index!] = last
70+
last.index = this.index!
71+
}
72+
}
5973
this.active = false
6074
}
6175
}
62-
63-
private recordEffectScope() {
64-
const parent = activeEffectScope
65-
if (parent && parent.active) {
66-
this.parent = parent
67-
this.parentIndex = parent.scopes.push(this) - 1
68-
}
69-
}
70-
71-
private derefChildScope(scope: EffectScope) {
72-
// reuse the freed index by moving the last array entry
73-
const last = this.scopes.pop()
74-
if (last && last !== scope) {
75-
const childIndex = scope.parentIndex!
76-
this.scopes[childIndex] = last
77-
last.parentIndex = childIndex
78-
}
79-
}
8076
}
8177

8278
export function effectScope(detached?: boolean) {

0 commit comments

Comments
 (0)