Repository: vuejs/vue
Pull request: add provide/inject on functional context

#5194 

Changed files:
filename: src/core/instance/inject.js
patch: @@ -14,11 +14,31 @@ export function initProvide (vm: Component) {
 }
 
 export function initInjections (vm: Component) {
-  const inject: any = vm.$options.inject
+  const result = resolveInject(vm.$options.inject, vm)
+  if (result) {
+    Object.keys(result).forEach(key => {
+      if (process.env.NODE_ENV !== 'production') {
+        defineReactive(vm, key, result[key], () => {
+          warn(
+            `Avoid mutating an injected value directly since the changes will be ` +
+            `overwritten whenever the provided component re-renders. ` +
+            `injection being mutated: "${key}"`,
+            vm
+          )
+        })
+      } else {
+        defineReactive(vm, key, result[key])
+      }
+    })
+  }
+}
+
+export function resolveInject (inject: any, vm: Component): ?Object {
   if (inject) {
     // inject is :any because flow is not smart enough to figure out cached
     // isArray here
     const isArray = Array.isArray(inject)
+    const result = Object.create(null)
     const keys = isArray
       ? inject
       : hasSymbol
@@ -49,5 +69,6 @@ export function initInjections (vm: Component) {
         source = source.$parent
       }
     }
+    return result
   }
 }

filename: src/core/vdom/create-component.js
patch: @@ -4,6 +4,7 @@ import VNode from './vnode'
 import { createElement } from './create-element'
 import { resolveConstructorOptions } from '../instance/init'
 import { resolveSlots } from '../instance/render-helpers/resolve-slots'
+import { resolveInject } from '../instance/inject'
 
 import {
   warn,
@@ -184,11 +185,15 @@ function createFunctionalComponent (
   // gets a unique context - this is necessary for correct named slot check
   const _context = Object.create(context)
   const h = (a, b, c, d) => createElement(_context, a, b, c, d, true)
+
+  // functional injections should not reactive
+  const injections = resolveInject(Ctor.options.inject, _context)
   const vnode = Ctor.options.render.call(null, h, {
     props,
     data,
     parent: context,
     children,
+    injections,
     slots: () => resolveSlots(children, context)
   })
   if (vnode instanceof VNode) {

filename: test/unit/features/options/inject.spec.js
patch: @@ -144,6 +144,29 @@ describe('Options provide/inject', () => {
     expect(child.baz).toBe(3)
   })
 
+  // Github issue #5194
+  it('should work with functional', () => {
+    new Vue({
+      template: `<child/>`,
+      provide: {
+        foo: 1,
+        bar: false
+      },
+      components: {
+        child: {
+          functional: true,
+          inject: ['foo', 'bar'],
+          render (h, context) {
+            const { injections } = context
+            injected = [injections.foo, injections.bar]
+          }
+        }
+      }
+    }).$mount()
+
+    expect(injected).toEqual([1, false])
+  })
+
   if (typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys)) {
     it('with Symbol keys', () => {
       const s = Symbol()

filename: types/options.d.ts
patch: @@ -69,6 +69,7 @@ export interface RenderContext {
   slots(): any;
   data: VNodeData;
   parent: Vue;
+  injections: any
 }
 
 export interface PropOptions {