Parent attributes rewrite prop values of children
Vue version
3.2.47
Link to minimal reproduction
Steps to reproduce
- Create a component tree where prop name of a grandchild is the same as attribute name for the child
App.vue
<script setup>
import Comp1 from './Comp1.vue';
</script>
<template>
<Comp1
attribute-name="Attribute value from the parent"
/>
</template>
Comp1.vue
<script setup>
import Comp2 from './Comp2.vue';
</script>
<template>
<Comp2
attribute-name="Prop value from the parent"
/>
</template>
Comp2.vue
<script setup>
const props = defineProps({
attributeName: {
type: String,
'default': 'Default value',
},
});
</script>
<template>
{{ attributeName }}
</template>
What is expected?
Parent attributes should not rewrite prop values of children
Expected value: "Prop value from the parent"
What is actually happening?
Grandchild gets value from attribute of its grandparent and not from template of its parent
Actual value: "Attribute value from the parent"
System Info
No response
Any additional comments?
No response
This is expected behavior.
We understand it can lead to some confusing situations in edge cases, but it's the way it was designed.
I also don't see real use cases where this could happen except when mistakenly adding attributes that should not have been added in the first place. Would be interested in examples where this happened from valid use
Either way though, changing it now would be a breaking change.
PTAL here
The problem here is that component's behavior depends on where we use it (which is not good)
even if we don't mean to rewrite that attribute it can be confused with html attributes and the user of the component just wanted to pass title html attribute for a button. but since internal component has the same prop name it will be rewritten. And there will be no indication of this rewriting because you will not have title
exposed in any of your API documentation since it is not a prop of the component. I understand it is an edge case, but the behavior of the component should not be affected by grandparent's environment. it should be only configurable within the scope of its parent.
Maybe at least you can add a warning about this behavior to avoid confusion?
I get your point, at least to an extent, but would like to understand the provided example better.
What would have been your expected behavior? Should it be ignored or somehow still passed down as an attribute?
- The former would be technically feasable (yet a breaking change without using
inheritAttrs: false
), - the latter would be a significant internal change on top of that, as vnodes only have a single props object in Vue 3 - props, attrs, events all come down as vnode props.
Also, in your particular example, it could be seen as wanted behavior that someone can pass a title (as an html attr) on the IconButton
and have the BaseButton
receive it - which it does, as a prop, but it receives it, likely to handle it a bit differently than the normal html title
.
Maybe at least you can add a warning about this behavior to avoid confusion?
Sure, the docs team would be thankful to receive an issue about this over at https://github.com/vuejs/docs
My point is that if internal component has such structure the grandparent can rewrite any of props for the internal component even if they are not meant to be public.
I expect that grandparent, whatever we pass in its attributes, should never affect internal logic of the component because of a naming collision with its child props (because sometimes we don't have information about which attributes we can pass), but I understand it is a breaking change for attribute fallthrough logic and suggested a warning message.
Also I meant to add a warning on the rendering level. if such rewriting happened the developer should know about it. If it is not possible I'll create an issue for the docs team.