diff --git a/packages/core/src/component.ts b/packages/core/src/component.ts
index 371cd915..6d5d4bfa 100644
--- a/packages/core/src/component.ts
+++ b/packages/core/src/component.ts
@@ -46,7 +46,7 @@ interface PublicInstanceMethods {
interface APIMethods
{
data?(): Partial
- render(props: Readonly, slots: Slots, attrs: Data): any
+ render(props: Readonly
, slots: Slots, attrs: Data, parentVNode: VNode): any
}
interface LifecycleMethods {
@@ -76,7 +76,7 @@ export interface ComponentClass extends ComponentClassOptions {
}
export interface FunctionalComponent
{
- (props: P, slots: Slots, attrs: Data): any
+ (props: P, slots: Slots, attrs: Data, parentVNode: VNode): any
pure?: boolean
props?: ComponentPropsOptions
displayName?: string
diff --git a/packages/core/src/componentUtils.ts b/packages/core/src/componentUtils.ts
index bb91a6b1..0aebc6b3 100644
--- a/packages/core/src/componentUtils.ts
+++ b/packages/core/src/componentUtils.ts
@@ -107,7 +107,8 @@ export function renderInstanceRoot(instance: ComponentInstance): VNode {
instance.$proxy,
instance.$props,
instance.$slots,
- instance.$attrs
+ instance.$attrs,
+ instance.$parentVNode
)
} catch (err) {
handleError(err, instance, ErrorTypes.RENDER)
@@ -120,7 +121,7 @@ export function renderFunctionalRoot(vnode: VNode): VNode {
const { props, attrs } = resolveProps(vnode.data, render.props)
let subTree
try {
- subTree = render(props, vnode.slots || EMPTY_OBJ, attrs || EMPTY_OBJ)
+ subTree = render(props, vnode.slots || EMPTY_OBJ, attrs || EMPTY_OBJ, vnode)
} catch (err) {
handleError(err, vnode, ErrorTypes.RENDER)
}
diff --git a/packages/core/src/optional/context.ts b/packages/core/src/optional/context.ts
index 6ab92fac..424bc5e0 100644
--- a/packages/core/src/optional/context.ts
+++ b/packages/core/src/optional/context.ts
@@ -1,8 +1,8 @@
import { observable } from '@vue/observer'
-import { Component } from '../component'
+import { Component, FunctionalComponent, ComponentInstance } from '../component'
import { warn } from '../warning'
-
-const contextStore = observable() as Record
+import { Slots, VNode } from '../vdom'
+import { VNodeFlags } from '../flags'
interface ProviderProps {
id: string | symbol
@@ -10,6 +10,8 @@ interface ProviderProps {
}
export class Provide extends Component {
+ context: Record = observable()
+
static props = {
id: {
type: [String, Symbol],
@@ -20,40 +22,75 @@ export class Provide extends Component {
}
}
- updateValue() {
- // TS doesn't allow symbol as index :/
- // https://github.com/Microsoft/TypeScript/issues/24587
- contextStore[this.$props.id as string] = this.$props.value
- }
created() {
- if (__DEV__) {
- const { id } = this.$props
- if (contextStore.hasOwnProperty(id)) {
- warn(`A context provider with id ${id.toString()} already exists.`)
+ const { $props, context } = this
+ this.$watch(
+ () => $props.value,
+ value => {
+ // TS doesn't allow symbol as index :/
+ // https://github.com/Microsoft/TypeScript/issues/24587
+ context[$props.id as string] = value
+ },
+ {
+ sync: true,
+ immediate: true
}
+ )
+ if (__DEV__) {
this.$watch(
- () => this.$props.id,
- (id: string, oldId: string) => {
+ () => $props.id,
+ (id, oldId) => {
warn(
`Context provider id change detected (from "${oldId}" to "${id}"). ` +
- `This is not supported and should be avoided.`
+ `This is not supported and should be avoided as it leads to ` +
+ `indeterministic context resolution.`
)
},
- { sync: true }
+ {
+ sync: true
+ }
)
}
- this.updateValue()
- }
- beforeUpdate() {
- this.updateValue()
}
+
render(props: any, slots: any) {
return slots.default && slots.default()
}
}
-export class Inject extends Component {
- render(props: any, slots: any) {
- return slots.default && slots.default(contextStore[props.id])
- }
+interface InjectProps {
+ id: string | symbol
}
+
+export const Inject: FunctionalComponent = (
+ props: InjectProps,
+ slots: Slots,
+ attrs: any,
+ vnode: VNode
+) => {
+ let resolvedValue
+ let resolved = false
+ const { id } = props
+ // walk the parent chain to locate context with key
+ while (vnode !== null && vnode.contextVNode !== null) {
+ if (
+ vnode.flags & VNodeFlags.COMPONENT_STATEFUL &&
+ (vnode.children as ComponentInstance).constructor === Provide &&
+ (vnode.children as any).context.hasOwnProperty(id)
+ ) {
+ resolved = true
+ resolvedValue = (vnode.children as any).context[id]
+ break
+ }
+ vnode = vnode.contextVNode
+ }
+ if (__DEV__ && !resolved) {
+ warn(
+ `Inject with id "${id.toString()}" did not match any Provider with ` +
+ `corresponding property in the parent chain.`
+ )
+ }
+ return slots.default && slots.default(resolvedValue)
+}
+
+Inject.pure = true
diff --git a/packages/core/src/warning.ts b/packages/core/src/warning.ts
index 6bda13d4..e014924a 100644
--- a/packages/core/src/warning.ts
+++ b/packages/core/src/warning.ts
@@ -14,7 +14,7 @@ export function popWarningContext() {
export function warn(msg: string, ...args: any[]) {
// TODO warn handler?
- warn(`[Vue warn]: ${msg}${getComponentTrace()}`, ...args)
+ console.warn(`[Vue warn]: ${msg}${getComponentTrace()}`, ...args)
}
function getComponentTrace(): string {