fix(reactivity): should add allowRecurse to the effect (#2213)
So that the scheduler also respects effect's allowRecurse option. fix #2200
This commit is contained in:
parent
28d5fd7a28
commit
ea1f87eabf
@ -17,6 +17,7 @@ export interface ReactiveEffect<T = any> {
|
|||||||
raw: () => T
|
raw: () => T
|
||||||
deps: Array<Dep>
|
deps: Array<Dep>
|
||||||
options: ReactiveEffectOptions
|
options: ReactiveEffectOptions
|
||||||
|
allowRecurse: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ReactiveEffectOptions {
|
export interface ReactiveEffectOptions {
|
||||||
@ -100,6 +101,7 @@ function createReactiveEffect<T = any>(
|
|||||||
}
|
}
|
||||||
} as ReactiveEffect
|
} as ReactiveEffect
|
||||||
effect.id = uid++
|
effect.id = uid++
|
||||||
|
effect.allowRecurse = !!options.allowRecurse
|
||||||
effect._isEffect = true
|
effect._isEffect = true
|
||||||
effect.active = true
|
effect.active = true
|
||||||
effect.raw = fn
|
effect.raw = fn
|
||||||
@ -180,7 +182,7 @@ export function trigger(
|
|||||||
const add = (effectsToAdd: Set<ReactiveEffect> | undefined) => {
|
const add = (effectsToAdd: Set<ReactiveEffect> | undefined) => {
|
||||||
if (effectsToAdd) {
|
if (effectsToAdd) {
|
||||||
effectsToAdd.forEach(effect => {
|
effectsToAdd.forEach(effect => {
|
||||||
if (effect !== activeEffect || effect.options.allowRecurse) {
|
if (effect !== activeEffect || effect.allowRecurse) {
|
||||||
effects.add(effect)
|
effects.add(effect)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -8,7 +8,9 @@ import {
|
|||||||
VNode,
|
VNode,
|
||||||
provide,
|
provide,
|
||||||
inject,
|
inject,
|
||||||
Ref
|
Ref,
|
||||||
|
watch,
|
||||||
|
SetupContext
|
||||||
} from '@vue/runtime-test'
|
} from '@vue/runtime-test'
|
||||||
|
|
||||||
describe('renderer: component', () => {
|
describe('renderer: component', () => {
|
||||||
@ -138,46 +140,6 @@ describe('renderer: component', () => {
|
|||||||
expect(serializeInner(root)).toBe(`<div>1</div><div>1</div>`)
|
expect(serializeInner(root)).toBe(`<div>1</div><div>1</div>`)
|
||||||
})
|
})
|
||||||
|
|
||||||
// #2170
|
|
||||||
test('should have access to instance’s “$el” property in watcher when setting instance data', async () => {
|
|
||||||
function returnThis(this: any) {
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
const dataWatchSpy = jest.fn(returnThis)
|
|
||||||
let instance: any
|
|
||||||
const Comp = {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
testData: undefined
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
testData() {
|
|
||||||
// @ts-ignore
|
|
||||||
dataWatchSpy(this.$el)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
instance = this
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return h('div')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const root = nodeOps.createElement('div')
|
|
||||||
render(h(Comp), root)
|
|
||||||
|
|
||||||
expect(dataWatchSpy).not.toHaveBeenCalled()
|
|
||||||
instance.testData = 'data'
|
|
||||||
|
|
||||||
await nextTick()
|
|
||||||
expect(dataWatchSpy).toHaveBeenCalledWith(instance.$el)
|
|
||||||
})
|
|
||||||
|
|
||||||
// #2170
|
// #2170
|
||||||
test('should have access to instance’s “$el” property in watcher when rendereing with watched prop', async () => {
|
test('should have access to instance’s “$el” property in watcher when rendereing with watched prop', async () => {
|
||||||
function returnThis(this: any) {
|
function returnThis(this: any) {
|
||||||
@ -207,7 +169,6 @@ describe('renderer: component', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const root = nodeOps.createElement('div')
|
const root = nodeOps.createElement('div')
|
||||||
|
|
||||||
render(h(Comp), root)
|
render(h(Comp), root)
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(propWatchSpy).not.toHaveBeenCalled()
|
expect(propWatchSpy).not.toHaveBeenCalled()
|
||||||
@ -216,4 +177,43 @@ describe('renderer: component', () => {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
expect(propWatchSpy).toHaveBeenCalledWith(instance.$el)
|
expect(propWatchSpy).toHaveBeenCalledWith(instance.$el)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #2200
|
||||||
|
test('component child updating parent state in pre-flush should trigger parent re-render', async () => {
|
||||||
|
const outer = ref(0)
|
||||||
|
const App = {
|
||||||
|
setup() {
|
||||||
|
const inner = ref(0)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
return [
|
||||||
|
h('div', inner.value),
|
||||||
|
h(Child, {
|
||||||
|
value: outer.value,
|
||||||
|
onUpdate: (val: number) => (inner.value = val)
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Child = {
|
||||||
|
props: ['value'],
|
||||||
|
setup(props: any, { emit }: SetupContext) {
|
||||||
|
watch(() => props.value, (val: number) => emit('update', val))
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
return h('div', props.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = nodeOps.createElement('div')
|
||||||
|
render(h(App), root)
|
||||||
|
expect(serializeInner(root)).toBe(`<div>0</div><div>0</div>`)
|
||||||
|
|
||||||
|
outer.value++
|
||||||
|
await nextTick()
|
||||||
|
expect(serializeInner(root)).toBe(`<div>1</div><div>1</div>`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user