Improve the props parameter in setup function
What problem does this feature solve?
import { defineComponent, toRefs, watch } from "vue";
export default defineComponent({
props: {
foo: Boolean
},
setup(props) {
const { foo } = toRefs(props);
// As `foo` might be undefined instead of a Ref
// we cannot watch it directly like this
watch(foo, () => { ... });
}
});
Instead, we need to use:
watch(() => props.foo, () => { ... });
So we need to switch to props.foo
whenever we need to watch it, which doesn't feel very natural to me.
What does the proposed API look like?
For all declared props, always provide { [propName]: undefined }
in the props
parameter of the setup
function so that we can write:
setup(props) { // props being { foo: undefined }
const { foo } = toRefs(props);
// foo is Ref
watch(foo, () => { ... });
}
How would you differentiate a prop passed as undefined
from a missing prop?
Wouldn't make more sense to do:
const foo = toRef(props, 'foo')
Edit: Although this is technically a breaking change, having the key with undefined
seems to be more flexible, especially with toRefs()
(which otherwise breaks). The limitation would be not being able to detect if a prop is missing when undefined
is an allowed value (not Booleans basically). e.g. if the user is passing undefined
, which I don't think one should do because they should be passing null
instead, to tell the component the prop is empty
If a prop
is missed, it will not appear in props:
{
props: {
foo: {
type: String
}
},
setup(props) {
console.log(Object.keys(props)) // []
const { foo } = toRefs(props) // foo is `undefined`
}
}
So I think it is as expected
How would you differentiate a prop passed as
undefined
from a missing prop?
@posva Hmmm this is a valid point. Maybe we do need to use toRef
on individual prop instead as you suggested.
@HcySunYang I think @posva is right if we always provide undefined
for missing prop there will be no way to tell if it comes from users.
So maybe we should only use toRefs
to convert required props (or with default values) to refs. I'm closing this one 😄
How would you differentiate a prop passed as undefined from a missing prop?
What would be the use case for an optional prop that wasn't passed in to the component?
<my-comp/>
<my-comp :foo="undefined"/>
<script>
props : {
foo: String
}
</script>
That code should behave the same.
This behaviour gets really inconsistent when you use a Boolean
instead of a String
example
This behaviour is different from v2 options API with options API the properties will always be defined. If this is intended it needs to be marked as breaking change from v2
// vue2
{
props: {
foo: String
},
data(){
return {
hasKey: 'foo' in this.$props // true
}
}
}
// vue3
{
props: {
foo: String
},
data(){
return {
hasKey: 'foo' in this.$props // false
}
}
}
Altho I still dont know the use case for handling an optional property without default (aka undefined
) differently from undefined
After some discussion with @pikax I think this issue should be reopened. As with current implementation, we actually cannot reliably detect missing props with a simple key in props
check, while improving the experience of toRefs
may bring more benefits.
This did break prop pass detection that I was relying on as reported in https://github.com/logaretm/vee-validate/pull/3294
As a workaround I had introduced a symbol as a default value to detect when the user didn't pass the prop:
export const EMPTY_VALUE = Symbol('Default empty value');
``
```js
{
modelValue: {
type: null,
default: EMPTY_VALUE,
},
}
This should probably be marked as a breaking change in the changelog.