Skip to content

Commit 7dea9f1

Browse files
Kingwlyyx990803
authored andcommitted
fix provide isn't reactive with a single array (#5229)
* fix provide isn't reactive with a single array - Fix #5223 * add warning when injections has been modified
1 parent 4c4a2ab commit 7dea9f1

File tree

2 files changed

+69
-1
lines changed

2 files changed

+69
-1
lines changed

src/core/instance/inject.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/* @flow */
22

33
import { hasSymbol } from 'core/util/env'
4+
import { warn } from '../util/index'
5+
import { defineReactive } from '../observer/index'
46

57
export function initProvide (vm: Component) {
68
const provide = vm.$options.provide
@@ -29,7 +31,18 @@ export function initInjections (vm: Component) {
2931
let source = vm
3032
while (source) {
3133
if (source._provided && provideKey in source._provided) {
32-
vm[key] = source._provided[provideKey]
34+
if (process.env.NODE_ENV !== 'production') {
35+
defineReactive(vm, key, source._provided[provideKey], () => {
36+
warn(
37+
`Avoid mutating a injections directly since the value will be ` +
38+
`overwritten whenever the provided component re-renders. ` +
39+
`injections being mutated: "${key}"`,
40+
vm
41+
)
42+
})
43+
} else {
44+
defineReactive(vm, key, source._provided[provideKey])
45+
}
3346
break
3447
}
3548
source = source.$parent

test/unit/features/options/inject.spec.js

+55
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,59 @@ describe('Options provide/inject', () => {
162162
expect(vm.$el.textContent).toBe('123')
163163
})
164164
}
165+
166+
// Github issue #5223
167+
it('should work with reactive array', done => {
168+
const vm = new Vue({
169+
template: `<div><child></child></div>`,
170+
data () {
171+
return {
172+
foo: []
173+
}
174+
},
175+
provide () {
176+
return {
177+
foo: this.foo
178+
}
179+
},
180+
components: {
181+
child: {
182+
inject: ['foo'],
183+
template: `<span>{{foo.length}}</span>`
184+
}
185+
}
186+
}).$mount()
187+
188+
expect(vm.$el.innerHTML).toEqual(`<span>0</span>`)
189+
vm.foo.push(vm.foo.length)
190+
vm.$nextTick(() => {
191+
expect(vm.$el.innerHTML).toEqual(`<span>1</span>`)
192+
vm.foo.pop()
193+
vm.$nextTick(() => {
194+
expect(vm.$el.innerHTML).toEqual(`<span>0</span>`)
195+
done()
196+
})
197+
})
198+
})
199+
200+
it('should warn when injections has been modified', () => {
201+
const key = 'foo'
202+
const vm = new Vue({
203+
provide: {
204+
foo: 1
205+
}
206+
})
207+
208+
const child = new Vue({
209+
parent: vm,
210+
inject: ['foo']
211+
})
212+
213+
expect(child.foo).toBe(1)
214+
child.foo = 2
215+
expect(
216+
`Avoid mutating a injections directly since the value will be ` +
217+
`overwritten whenever the provided component re-renders. ` +
218+
`injections being mutated: "${key}"`).toHaveBeenWarned()
219+
})
165220
})

0 commit comments

Comments
 (0)