Skip to content

Commit 76fcde1

Browse files
committed
fix(ssr): watchEffect onInvalidate runner initialization
1 parent 1dedc19 commit 76fcde1

File tree

2 files changed

+36
-6
lines changed

2 files changed

+36
-6
lines changed

packages/runtime-core/__tests__/apiWatch.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
ComponentInternalInstance,
1111
ComponentPublicInstance
1212
} from '../src/index'
13+
import { setupComponent } from '../src/component'
1314
import {
1415
render,
1516
nodeOps,
@@ -44,6 +45,28 @@ describe('api: watch', () => {
4445
expect(dummy).toBe(1)
4546
})
4647

48+
// https://github.com/vuejs/vue-next/issues/3322
49+
it('effect onInvalidate succeeds in ssr context', done => {
50+
// XXX do this without double setup and generally less hackily!
51+
let instance: ComponentInternalInstance | null
52+
const noop = () => {}
53+
const Comp = defineComponent({
54+
setup() {
55+
if (!instance) {
56+
instance = getCurrentInstance()
57+
} else {
58+
watchEffect(onInvalidate => {
59+
expect(() => onInvalidate(noop)).not.toThrow(ReferenceError)
60+
done()
61+
})
62+
}
63+
},
64+
render: noop
65+
})
66+
createApp(Comp).mount(nodeOps.createElement('div'))
67+
setupComponent(instance!, true)
68+
})
69+
4770
it('watching single source: getter', async () => {
4871
const state = reactive({ count: 0 })
4972
let dummy

packages/runtime-core/src/apiWatch.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import {
55
Ref,
66
ComputedRef,
77
ReactiveEffectOptions,
8-
isReactive
8+
isReactive,
9+
ReactiveEffect
910
} from '@vue/reactivity'
1011
import { SchedulerJob, queuePreFlushCb } from './scheduler'
1112
import {
@@ -167,6 +168,7 @@ function doWatch(
167168

168169
let getter: () => any
169170
let forceTrigger = false
171+
let runner: ReactiveEffect<any> | undefined
170172
if (isRef(source)) {
171173
getter = () => (source as Ref).value
172174
forceTrigger = !!(source as Ref)._shallow
@@ -224,6 +226,9 @@ function doWatch(
224226

225227
let cleanup: () => void
226228
const onInvalidate: InvalidateCbRegistrator = (fn: () => void) => {
229+
if (!runner) {
230+
return
231+
}
227232
cleanup = runner.options.onStop = () => {
228233
callWithErrorHandling(fn, instance, ErrorCodes.WATCH_CLEANUP)
229234
}
@@ -246,7 +251,7 @@ function doWatch(
246251

247252
let oldValue = isArray(source) ? [] : INITIAL_WATCHER_VALUE
248253
const job: SchedulerJob = () => {
249-
if (!runner.active) {
254+
if (!runner || !runner.active) {
250255
return
251256
}
252257
if (cb) {
@@ -293,7 +298,7 @@ function doWatch(
293298
}
294299
}
295300

296-
const runner = effect(getter, {
301+
runner = effect(getter, {
297302
lazy: true,
298303
onTrack,
299304
onTrigger,
@@ -316,9 +321,11 @@ function doWatch(
316321
}
317322

318323
return () => {
319-
stop(runner)
320-
if (instance) {
321-
remove(instance.effects!, runner)
324+
if (runner) {
325+
stop(runner)
326+
if (instance) {
327+
remove(instance.effects!, runner)
328+
}
322329
}
323330
}
324331
}

0 commit comments

Comments
 (0)