-
Notifications
You must be signed in to change notification settings - Fork 429
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Composition functions support and this
value changes
#416
Comments
I really like it. Well thoughts. One question is would it be possible to pass another class property to the composition function? This might not be the best example, but something like this. See function useCounter (amount: number) {
const count = ref(0)
function increment () {
count.value + amount
}
return {
count,
increment
}
}
export default class Counter extends Vue {
amount = 10
get counter () {
reactive(useCounter(this.amount))
}
} |
@kiaking You mean like the following code? (passing primitive loses its reactivity and computed does not handle composition functions in this proposal) function useCounter (amount: Ref<number>) {
const count = ref(0)
function increment () {
count.value + amount.value
}
return {
count,
increment
}
}
export default class Counter extends Vue {
amount = 10
counter = reactive(useCounter(
toRef(this, 'amount')
))
} I think we should discourage this usage because I would say the dependency direction between composition function and component instance should be composition <- instance. This is as same as the canonical Vue api ( function useCounter () {
const amount = ref(10)
const count = ref(0)
function increment () {
count.value + amount.value
}
return {
count,
amount,
increment
}
}
export default class Counter extends Vue {
counter = reactive(useCounter())
mounted() {
this.counter.amount = 11
}
} |
Ah OK good point. Yes I was afraid of that. I don't have good example here but I suspected there might be shared 3rd party composition function that might require argument to be passed. But as you mentioned, we could always "wrap" such composition function to be used before assigning to the class component. // 3rd party composition function. It requires argument.
function useCounter (amount: Ref<number>) {
const count = ref(0)
function increment () {
count.value + amount.value
}
return { count, increment }
}
// Wrap the above composition function.
function useWrappedCounter () {
const amount = ref(10)
return {
amount,
...useCounter(amount)
}
}
// Use wrapped function.
export default class Counter extends Vue {
counter = reactive(useWrappedCounter())
mounted() {
this.counter.amount = 11
}
} |
An idea to allow such usecase would be to require wrapping a composition function and delay its initalization: export default class Counter extends Vue {
amount = 10
counter = setup(() => {
return useCounter(toRef(this, 'amount'))
})
} The current problems of
To solve 1. we can just configure proxy getter / setter to actual reactive data for each initialized property. As for 2. we need to delay the invocation of composition function to make sure accessed properties are converted reactive value. We can solve 2. with I noticed that mandating |
I have updated the proposal regarding the above |
Nice! I really dig the new |
Since Vue core changed the unwrapping behavior of |
Since setup() for variables used on the state add a bunch a unwanted boilerplate I am asking similarly as for props, would there be ways to define (state) variables used on the template inside the class ? |
@LifeIsStrange I'm afraid I don't understand what you mean. Could you elaborate "ways to define (state) variables used on the template inside the class"? I'm not sure how we can integrate class component with the new |
Please file a new story for a bug report. Thanks. |
<template>
<div>Count: {{ counter.count }}</div>
<button @click="counter.increment()">+</button>
</template>
<script lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { Vue, setup } from 'vue-class-component'
function useCounter () {
const count = ref(0)
function increment () {
count.value++
}
onMounted(() => {
console.log('onMounted')
})
return {
count,
increment
}
}
export default class Counter extends Vue {
counter = setup(() => useCounter())
}
</script> If I understand correctly, this (your) example show how we can make hybrid class components using composition functions, which is nice even if I don't get much the value of it because part of the logic is outside the class. <template>
<div>Count: {{ count }}</div>
<button @click="increment()">+</button>
</template>
<script lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { Vue, setup } from 'vue-class-component'
export default class Counter extends Vue {
count = 0
increment () {
this.count++
}
}
</script> If so do we have guarantee that this existing way will remain supported for the foreesable future and not be deprecated by the composing function api ? |
@LifeIsStrange You can still do the existing way. The purpose of adding the |
<template>
<div>Count: {{ counter.count }}</div>
<button @click="counter.increment()">+</button>
</template>
<script lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { Vue, setup } from 'vue-class-component'
function useCounter () {
const count = ref(0)
function increment () {
count.value++
}
onMounted(() => {
console.log('onMounted')
})
return {
count,
increment
}
}
export default class Counter extends Vue {
counter = setup(() => useCounter())
}
</script> For me this approach looks too verbose and a little bit strange. Why are we assiging <template>
<div>Count: {{ counter.count }}</div>
<button @click="counter.increment()">+</button>
</template>
<script lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { Vue, setup } from 'vue-class-component'
function useCounter () {
const count = ref(0)
function increment () {
count.value++
}
onMounted(() => {
console.log('onMounted')
})
return {
count,
increment
}
}
export default class Counter extends Vue {
setup(props) {
const { count, increment } = useCounter();
return {
count,
increment
};
}
}
</script> |
It won't be typed in that way as class type can't be changed from method itself. |
Ok. Thanks for your answer. I have another question. Can you change that the Using Also
Additionally |
Why don't you just use props value under
Even though it's exactly as same as normal Vue component API?
This should be covered in another feature. |
@ktsn I have a question about the alternative approach
What if I want to have a class component which extends composition function and also have props? I wrote the following component: <template>
<div>{{ msg }}</div>
<div>{{ counter.count }}</div>
<button @click="counter.increment">Increment</button>
<div>{{ secondCounter.count }}</div>
<button @click="secondCounter.increment">Increment</button>
</template>
<script lang="ts">
import { onMounted, ref } from "vue";
import { Vue, prop, setup, mixins } from "vue-class-component";
class Props {
msg = prop({ type: String, required: true });
}
const Super = setup(() => {
const text = ref('A')
const addLetter = () => {
text.value += 'A';
}
return {
text,
addLetter
}
})
export default class HelloWorld extends mixins(Super).with(Props) {
private counter = setup(() => {
const count = ref(0);
console.log("MSG", this.msg);
const increment = () => {
count.value++;
};
onMounted(() => {
console.log("onMounted first counter");
});
return {
count,
increment,
};
});
private secondCounter = setup(() => {
const count = ref(0);
const increment = () => {
count.value++;
};
onMounted(() => {
console.log("onMounted second counter");
});
return {
count,
increment,
};
});
}
</script>
<style>
</style> However, I receive this error: |
@ktsn |
Please open a new issue. That's a bug. |
is support provide in compositionAPI ? how to use provide in compositionAPI |
Is it wrong word in "export default class App extends Setup" ? Error Info:
|
Couldn't the |
Will there be another way of implementing setup that makes the returned properties accesible in this? (aside from the current alternative one). Maybe defining in in the @options decorator? |
@soylomass Same answer for you too: decorators cannot modify the type of a See here: microsoft/TypeScript#4881 and microsoft/TypeScript#4881 (comment) Of course this is all possible in plain JS, but this library also supports TypeScript users and must work within those limitations (a subset of JS). |
@ktsn Idea: the return type of a method can be used as the type of any other class property. Suppose the // In the Vue definition:
class Vue {
constructor(...) {
this.#setupComposedProperty(this.setup(...))
}
composed: ReturnType<this['setup']>
setup(): object { return {} } // default
#setupComposedProperty(...) {...} // internal
} Then users can write the following: <script>
// User code
export default class Counter extends Vue {
setup(...) {
const { count, increment } = useCounter();
return {
count,
increment
};
}
}
</script>
<template>
<div>{{ composed.count }}</div>
</template> The This seems pretty clean compared to wrapping class field initializers with @lgarczyn @soylomass @Mikilll94 I think this would satisfy the desires you had, in a slightly different way. |
What about this problem? Why doesn't setup work when I use it in options |
Summary
setup
helper.setup
function under the hood.$props
(and its derived prop values),$attrs
,$slots
and$emit
are available onthis
in class property initializers.Example:
Details
Prior to v7, class component constructor is initialized in
data
hook to collect class properties. In v8, it will be initialized insetup
hook so that the users can use composition function in class property initializer.The above class component definition is as same as following canonical component definition.
setup
helperWrapping a composition function with
setup
helper is needed because we need to delay the invocation of the composition function. Let's see the following example:In the above example,
this.postId
will be referred bywatch
helper to track reactive dependencies immediately but it is not reactive value at that moment. Then the watch callback won't be called whenpostId
is changed.setup
helper will delay the invocation untilthis.postId
become a proxy property to the actual reactive value.setup
unwrappingAs same as
setup
in the Vue core library,setup
in Vue Class Component unwrapsref
values. The unwrapping happens shallowly:In addition, if you return a single ref in
setup
helper, the ref will also be unwrapped:Available built in properties on
this
Since the class constructor is used in
setup
hook, only following properties are available onthis
.$props
this
as well. (e.g.this.$props.foo
->this.foo
)$emit
$attrs
$slots
Example using
$props
and$emit
in a composition function.Alternative Approach
Another possible approach is using super class and mixins.
Pros
this
.Cons
Need duplicated props type definition.
The text was updated successfully, but these errors were encountered: