Skip to content

Commit 5a279be

Browse files
feat: add pause/resume function to ReactiveEffect
1 parent 2ffe3d5 commit 5a279be

File tree

3 files changed

+71
-0
lines changed

3 files changed

+71
-0
lines changed

packages/reactivity/__tests__/effect.spec.ts

+39
Original file line numberDiff line numberDiff line change
@@ -990,5 +990,44 @@ describe('reactivity/effect', () => {
990990
expect(fnSpy).toHaveBeenCalledTimes(3)
991991
expect(has).toBe(false)
992992
})
993+
994+
test('pause execution of side effect functions', () => {
995+
const obj = reactive({ foo: 1 })
996+
const fnSpy = vi.fn(() => obj.foo)
997+
998+
const runner = effect(fnSpy)
999+
1000+
expect(fnSpy).toHaveBeenCalledTimes(1)
1001+
obj.foo++
1002+
expect(fnSpy).toHaveBeenCalledTimes(2)
1003+
1004+
runner.effect.pause()
1005+
obj.foo++
1006+
expect(fnSpy).toHaveBeenCalledTimes(2)
1007+
1008+
runner.effect.resume()
1009+
1010+
obj.foo++
1011+
expect(fnSpy).toHaveBeenCalledTimes(3)
1012+
})
1013+
1014+
test('immediately execute the calls during the pause when resuming', () => {
1015+
const obj = reactive({ bar: 1 })
1016+
const fnSpy = vi.fn(() => obj.bar)
1017+
1018+
const runner = effect(fnSpy)
1019+
1020+
expect(fnSpy).toHaveBeenCalledTimes(1)
1021+
obj.bar++
1022+
expect(fnSpy).toHaveBeenCalledTimes(2)
1023+
1024+
runner.effect.pause()
1025+
obj.bar++
1026+
expect(fnSpy).toHaveBeenCalledTimes(2)
1027+
1028+
runner.effect.resume(true)
1029+
1030+
expect(fnSpy).toHaveBeenCalledTimes(3)
1031+
})
9931032
})
9941033
})

packages/reactivity/src/effect.ts

+28
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,16 @@ export class ReactiveEffect<T = any> {
6868
* @internal
6969
*/
7070
private deferStop?: boolean
71+
/**
72+
* Whether to pause
73+
* @internal
74+
*/
75+
private isPaused = false
76+
/**
77+
* Indicates whether the run method was called during the pause process
78+
* @internal
79+
*/
80+
private isCalled = false
7181

7282
onStop?: () => void
7383
// dev only
@@ -83,7 +93,23 @@ export class ReactiveEffect<T = any> {
8393
recordEffectScope(this, scope)
8494
}
8595

96+
pause() {
97+
this.isPaused = true
98+
}
99+
100+
resume(runOnResume = false) {
101+
this.isPaused = false
102+
if (runOnResume && this.isCalled) {
103+
this.run()
104+
}
105+
this.isCalled = false
106+
}
107+
86108
run() {
109+
if (this.isPaused) {
110+
this.isCalled = true
111+
return
112+
}
87113
if (!this.active) {
88114
return this.fn()
89115
}
@@ -126,6 +152,8 @@ export class ReactiveEffect<T = any> {
126152
}
127153

128154
stop() {
155+
// Reset the paused state first when stopping
156+
this.resume()
129157
// stopped while running itself - defer the cleanup
130158
if (activeEffect === this) {
131159
this.deferStop = true

packages/runtime-core/src/components/KeepAlive.ts

+4
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ const KeepAliveImpl: ComponentOptions = {
127127

128128
sharedContext.activate = (vnode, container, anchor, isSVG, optimized) => {
129129
const instance = vnode.component!
130+
// on activation, resume the effect of the component instance and immediately execute the call during the pause process
131+
instance.effect.resume(true)
130132
move(vnode, container, anchor, MoveType.ENTER, parentSuspense)
131133
// in case props have changed
132134
patch(
@@ -159,6 +161,8 @@ const KeepAliveImpl: ComponentOptions = {
159161

160162
sharedContext.deactivate = (vnode: VNode) => {
161163
const instance = vnode.component!
164+
// on deactivation, pause the effect of the component instance
165+
instance.effect.pause()
162166
move(vnode, storageContainer, null, MoveType.LEAVE, parentSuspense)
163167
queuePostRenderEffect(() => {
164168
if (instance.da) {

0 commit comments

Comments
 (0)