$parent doesn't skip transition components
Version
3.0.1
Reproduction link
https://codesandbox.io/s/clever-paper-9zw1d?file=/src/components/HelloWorld.vue
Steps to reproduce
- Open the production link
- Watch the console
What is expected?
console output:
plain App
in Transition App
in Keep-Alive App
in Teleport App
What is actually happening?
console output
plain App
in Transition BaseTransition
in Keep-Alive App
in Teleport App
In Vue 2, the transition component was "abstract" and $parent skipped it, so components could reach the "real" parent component.
Now, the parent of a component nested in an transition is a BaseTransition
instance, and, adding to the problem, this component doesn't have a public proxy with a $parent
property, so developers can't even manually skip it by calling this.$parent.$parent
In v3 there really isn't the concept of "abstract" components (i.e. ones that are omitted from parent chains) anymore, so this is expected behavior (and yes, technically a breaking change from v2, but the idea is that the usage of $parent
should be strongly discouraged in v3 with Composition APIs)
I think the actual issue here is that Transition
is a functional HOC which doesn't even have a public instance proxy. Or more generally, any presence of a functional component in the parent chain would prevent the child from traversing all the way to the root via $parent
.
Note: In v2, functional components do not show up in the parent chain at all. To fix this we should probably make the $parent
getter ignore functional parents (like in v2).
I think the actual issue here is that Transition is a functional HOC which doesn't even have a public instance proxy. Or more generally, any presence of a functional component in the parent chain would prevent the child from traversing all the way to the root via $parent.
I'm not sure that's exactly the issue here. Functional components don't have instances, they are only represented by vnodes. instance.parent
would never point to a vnode, it always points to a ComponentInternalInstance
(at least that's what the types say).
The parent that is returned by instance.parent
here is { type: {name: 'Basecomponent' }, ... }
, or in other words, $parent
doesn't seem to point to the functional component from runtime-dom, but rather the the BaseTransition
component from runtime-core, which the runtime-dom functional component wraps.
From my tests, doing this:
if (parent.type.name == 'BaseTransition') {
parent = parent.parent
}
would give me the parent component that contains the <transition>
transition, which is the component we want. Of course that implementation might be a bit naive, but that's the gist of it.
You are using $parent
which is a special getter on the public proxy, not instance.parent
on the internal instance.
The internal .parent
chain works (and includes functional components), but there is no equivalent in v2. The problematic part is the .$parent
chain on the public instance which currently has a different behavior in v3.
That's why I'm suggesting the fix is going further up the parent chain when $parent
encounters a functional component (i.e. an internal instance whose proxy
is null
).