Skip to content

Commit ea1f87e

Browse files
authored
fix(reactivity): should add allowRecurse to the effect (#2213)
So that the scheduler also respects effect's allowRecurse option. fix #2200
1 parent 28d5fd7 commit ea1f87e

File tree

2 files changed

+43
-41
lines changed

2 files changed

+43
-41
lines changed

packages/reactivity/src/effect.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export interface ReactiveEffect<T = any> {
1717
raw: () => T
1818
deps: Array<Dep>
1919
options: ReactiveEffectOptions
20+
allowRecurse: boolean
2021
}
2122

2223
export interface ReactiveEffectOptions {
@@ -100,6 +101,7 @@ function createReactiveEffect<T = any>(
100101
}
101102
} as ReactiveEffect
102103
effect.id = uid++
104+
effect.allowRecurse = !!options.allowRecurse
103105
effect._isEffect = true
104106
effect.active = true
105107
effect.raw = fn
@@ -180,7 +182,7 @@ export function trigger(
180182
const add = (effectsToAdd: Set<ReactiveEffect> | undefined) => {
181183
if (effectsToAdd) {
182184
effectsToAdd.forEach(effect => {
183-
if (effect !== activeEffect || effect.options.allowRecurse) {
185+
if (effect !== activeEffect || effect.allowRecurse) {
184186
effects.add(effect)
185187
}
186188
})

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

+40-40
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import {
88
VNode,
99
provide,
1010
inject,
11-
Ref
11+
Ref,
12+
watch,
13+
SetupContext
1214
} from '@vue/runtime-test'
1315

1416
describe('renderer: component', () => {
@@ -139,23 +141,21 @@ describe('renderer: component', () => {
139141
})
140142

141143
// #2170
142-
test('should have access to instance’s “$el” property in watcher when setting instance data', async () => {
144+
test('should have access to instance’s “$el” property in watcher when rendereing with watched prop', async () => {
143145
function returnThis(this: any) {
144146
return this
145147
}
146-
const dataWatchSpy = jest.fn(returnThis)
148+
const propWatchSpy = jest.fn(returnThis)
147149
let instance: any
148150
const Comp = {
149-
data() {
150-
return {
151-
testData: undefined
152-
}
151+
props: {
152+
testProp: String
153153
},
154154

155155
watch: {
156-
testData() {
156+
testProp() {
157157
// @ts-ignore
158-
dataWatchSpy(this.$el)
158+
propWatchSpy(this.$el)
159159
}
160160
},
161161

@@ -170,50 +170,50 @@ describe('renderer: component', () => {
170170

171171
const root = nodeOps.createElement('div')
172172
render(h(Comp), root)
173+
await nextTick()
174+
expect(propWatchSpy).not.toHaveBeenCalled()
173175

174-
expect(dataWatchSpy).not.toHaveBeenCalled()
175-
instance.testData = 'data'
176-
176+
render(h(Comp, { testProp: 'prop ' }), root)
177177
await nextTick()
178-
expect(dataWatchSpy).toHaveBeenCalledWith(instance.$el)
178+
expect(propWatchSpy).toHaveBeenCalledWith(instance.$el)
179179
})
180180

181-
// #2170
182-
test('should have access to instance’s “$el” property in watcher when rendereing with watched prop', async () => {
183-
function returnThis(this: any) {
184-
return this
185-
}
186-
const propWatchSpy = jest.fn(returnThis)
187-
let instance: any
188-
const Comp = {
189-
props: {
190-
testProp: String
191-
},
181+
// #2200
182+
test('component child updating parent state in pre-flush should trigger parent re-render', async () => {
183+
const outer = ref(0)
184+
const App = {
185+
setup() {
186+
const inner = ref(0)
192187

193-
watch: {
194-
testProp() {
195-
// @ts-ignore
196-
propWatchSpy(this.$el)
188+
return () => {
189+
return [
190+
h('div', inner.value),
191+
h(Child, {
192+
value: outer.value,
193+
onUpdate: (val: number) => (inner.value = val)
194+
})
195+
]
197196
}
198-
},
197+
}
198+
}
199199

200-
created() {
201-
instance = this
202-
},
200+
const Child = {
201+
props: ['value'],
202+
setup(props: any, { emit }: SetupContext) {
203+
watch(() => props.value, (val: number) => emit('update', val))
203204

204-
render() {
205-
return h('div')
205+
return () => {
206+
return h('div', props.value)
207+
}
206208
}
207209
}
208210

209211
const root = nodeOps.createElement('div')
212+
render(h(App), root)
213+
expect(serializeInner(root)).toBe(`<div>0</div><div>0</div>`)
210214

211-
render(h(Comp), root)
212-
await nextTick()
213-
expect(propWatchSpy).not.toHaveBeenCalled()
214-
215-
render(h(Comp, { testProp: 'prop ' }), root)
215+
outer.value++
216216
await nextTick()
217-
expect(propWatchSpy).toHaveBeenCalledWith(instance.$el)
217+
expect(serializeInner(root)).toBe(`<div>1</div><div>1</div>`)
218218
})
219219
})

0 commit comments

Comments
 (0)