Repository: vuejs/vue
Pull request: fix provide isn't reactive with a single array, Fix #5223 

<!--
Please make sure to read the Pull Request Guidelines:
https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#pull-request-guidelines
-->

<!-- PULL REQUEST TEMPLATE -->
<!-- (Update "[ ]" to "[x]" to check a box) -->

**What kind of change does this PR introduce?** (check at least one)

- [x] Bugfix
- [ ] Feature
- [ ] Code style update
- [ ] Refactor
- [ ] Build-related changes
- [ ] Other, please describe:

**Does this PR introduce a breaking change?** (check one)

- [ ] Yes
- [x] No

If yes, please describe the impact and migration path for existing applications:

**The PR fulfills these requirements:**

- [x] It's submitted to the `dev` branch for v2.x (or to a previous version branch), _not_ the `master` branch
- [ ] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number)
- [ ] All tests are passing: https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#development-setup
- [ ] New/updated tests are included

If adding a **new feature**, the PR's description includes:
- [x] A convincing reason for adding this feature (to avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it)

**Other information:**
#5223 

Changed files:
filename: src/core/instance/inject.js
patch: @@ -1,6 +1,8 @@
 /* @flow */
 
 import { hasSymbol } from 'core/util/env'
+import { warn } from '../util/index'
+import { defineReactive } from '../observer/index'
 
 export function initProvide (vm: Component) {
   const provide = vm.$options.provide
@@ -29,7 +31,18 @@ export function initInjections (vm: Component) {
       let source = vm
       while (source) {
         if (source._provided && provideKey in source._provided) {
-          vm[key] = source._provided[provideKey]
+          if (process.env.NODE_ENV !== 'production') {
+            defineReactive(vm, key, source._provided[provideKey], () => {
+              warn(
+                `Avoid mutating a injections directly since the value will be ` +
+                `overwritten whenever the provided component re-renders. ` +
+                `injections being mutated: "${key}"`,
+                vm
+              )
+            })
+          } else {
+            defineReactive(vm, key, source._provided[provideKey])
+          }
           break
         }
         source = source.$parent

filename: test/unit/features/options/inject.spec.js
patch: @@ -162,4 +162,59 @@ describe('Options provide/inject', () => {
       expect(vm.$el.textContent).toBe('123')
     })
   }
+
+  // Github issue #5223
+  it('should work with reactive array', done => {
+    const vm = new Vue({
+      template: `<div><child></child></div>`,
+      data () {
+        return {
+          foo: []
+        }
+      },
+      provide () {
+        return {
+          foo: this.foo
+        }
+      },
+      components: {
+        child: {
+          inject: ['foo'],
+          template: `<span>{{foo.length}}</span>`
+        }
+      }
+    }).$mount()
+
+    expect(vm.$el.innerHTML).toEqual(`<span>0</span>`)
+    vm.foo.push(vm.foo.length)
+    vm.$nextTick(() => {
+      expect(vm.$el.innerHTML).toEqual(`<span>1</span>`)
+      vm.foo.pop()
+      vm.$nextTick(() => {
+        expect(vm.$el.innerHTML).toEqual(`<span>0</span>`)
+        done()
+      })
+    })
+  })
+
+  it('should warn when injections has been modified', () => {
+    const key = 'foo'
+    const vm = new Vue({
+      provide: {
+        foo: 1
+      }
+    })
+
+    const child = new Vue({
+      parent: vm,
+      inject: ['foo']
+    })
+
+    expect(child.foo).toBe(1)
+    child.foo = 2
+    expect(
+      `Avoid mutating a injections directly since the value will be ` +
+      `overwritten whenever the provided component re-renders. ` +
+      `injections being mutated: "${key}"`).toHaveBeenWarned()
+  })
 })