Subscribe on changes!

$parent doesn't skip transition components

avatar
Oct 19th 2020

Version

3.0.1

Reproduction link

https://codesandbox.io/s/clever-paper-9zw1d?file=/src/components/HelloWorld.vue

Steps to reproduce

  1. Open the production link
  2. 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 BaseTransitioninstance, 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

avatar
Oct 19th 2020

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).

avatar
Oct 19th 2020

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.

avatar
Oct 19th 2020

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.

avatar
Oct 19th 2020

I know. $parent is using instance.parent.proxy.

But that fails when a BaseTransition is the parent.

avatar
Oct 20th 2020

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).

avatar
Oct 20th 2020

Got it.