Skip to content

Commit

Permalink
fix(runtime-core): ensure watchers are always registered to correct i…
Browse files Browse the repository at this point in the history
…nstance owner (#2495)

close: #2381
  • Loading branch information
LinusBorg committed Nov 27, 2020
1 parent ce4915d commit 735af1c
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 7 deletions.
64 changes: 61 additions & 3 deletions packages/runtime-core/__tests__/apiWatch.spec.ts
Expand Up @@ -5,16 +5,26 @@ import {
computed,
nextTick,
ref,
h
defineComponent,
getCurrentInstance,
ComponentInternalInstance,
ComponentPublicInstance
} from '../src/index'
import { render, nodeOps, serializeInner, TestElement } from '@vue/runtime-test'
import {
render,
nodeOps,
serializeInner,
TestElement,
h
} from '@vue/runtime-test'
import {
ITERATE_KEY,
DebuggerEvent,
TrackOpTypes,
TriggerOpTypes,
triggerRef,
shallowRef
shallowRef,
Ref
} from '@vue/reactivity'

// reference: https://vue-composition-api-rfc.netlify.com/api.html#watch
Expand Down Expand Up @@ -799,4 +809,52 @@ describe('api: watch', () => {
await nextTick()
expect(spy).toHaveBeenCalledTimes(1)
})

// https://github.com/vuejs/vue-next/issues/2381
test('$watch should always register its effects with itw own instance', async () => {
let instance: ComponentInternalInstance | null
let _show: Ref<boolean>

const Child = defineComponent({
render: () => h('div'),
mounted() {
instance = getCurrentInstance()
},
unmounted() {}
})

const Comp = defineComponent({
setup() {
const comp = ref<ComponentPublicInstance | undefined>()
const show = ref(true)
_show = show
return { comp, show }
},
render() {
return this.show
? h(Child, {
ref: vm => void (this.comp = vm as ComponentPublicInstance)
})
: null
},
mounted() {
// this call runs while Comp is currentInstance, but
// the effect for this `$watch` should nontheless be registered with Child
this.comp!.$watch(() => this.show, () => void 0)
}
})

render(h(Comp), nodeOps.createElement('div'))

expect(instance!).toBeDefined()
expect(instance!.effects).toBeInstanceOf(Array)
expect(instance!.effects!.length).toBe(1)

_show!.value = false

await nextTick()
await nextTick()

expect(instance!.effects![0].active).toBe(false)
})
})
2 changes: 1 addition & 1 deletion packages/runtime-core/src/apiWatch.ts
Expand Up @@ -285,7 +285,7 @@ function doWatch(
scheduler
})

recordInstanceBoundEffect(runner)
recordInstanceBoundEffect(runner, instance)

// initial run
if (cb) {
Expand Down
9 changes: 6 additions & 3 deletions packages/runtime-core/src/component.ts
Expand Up @@ -786,9 +786,12 @@ export function createSetupContext(

// record effects created during a component's setup() so that they can be
// stopped when the component unmounts
export function recordInstanceBoundEffect(effect: ReactiveEffect) {
if (currentInstance) {
;(currentInstance.effects || (currentInstance.effects = [])).push(effect)
export function recordInstanceBoundEffect(
effect: ReactiveEffect,
instance = currentInstance
) {
if (instance) {
;(instance.effects || (instance.effects = [])).push(effect)
}
}

Expand Down

0 comments on commit 735af1c

Please sign in to comment.