-
-
Notifications
You must be signed in to change notification settings - Fork 8.4k
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 API in a functional component #1645
Comments
Functional components do not have state so this should be expected. |
@CyberAP Yeah that totally makes sense. Just makes me curious whether there would be any major performance impacts in having functional components be stateful. |
@baryla I believe they state in the docs that performance difference is negligible for functional components, so Vue 3 migration guide recommends using stateful components |
@icehaunter thanks for the link buddy. Yeah I read that part which is fair enough but I thought that maybe we can remove that extra indentation that we have with |
I thought functional components would support the Composition API, according to rfc0008. |
Right now you can either define your refs in a higher scope - putting const counter = ref(0) in module scope raises the counter, since the function of the FC is invoked whenever the ref updates. So this doesn't work: function useCounter() {
const counter = ref(0);
function increment() {
counter.value++;
}
return { counter, increment };
} while this actually does: const counter = ref(0);
function useCounter() {
function increment() {
counter.value++;
}
return { counter, increment };
} This has two issues though:
I wonder why this isn't possible then (returning a function which returns the vnode), just like setup does: const Counter = () => {
const { counter, increment } = useCounter()
return () => <button onClick={increment}>Counter: {counter.value}</button>
}; But FunctionalComponents don't seem to support that, so it won't work. const defineSetupComponent = (setupFn) => defineComponent({ name: setupFn.name, setup: setupFn })
export default defineSetupComponent(Counter); which works, but I still think all of this will confuse a lot of developers, especially those coming from React. |
Shouldn't this at least be caught by eslint and be documented to avoid confusion? |
@bjarkihall if you need to simplify your component you can use setup to return a render function, instead of creating a Functional Component for that: export default {
setup() {
const counter = ref(0)
const increment = () => counter.value++
return () => [
h('div', counter),
h('button', { onClick: increment })
]
}
} |
If you don't care about having the So it seems like you could do this to define a stateful component without making your own export default defineComponent(() => {
const { counter, increment } = useCounter();
return () => <button onClick={increment}>Counter: {counter.value}</button>;
}) |
@bjarkihall that's very interesting indeed and I totally agree that it may be confusing for React devs wanting to try Vue but it's all down to the direction of Vue. Very nice writeup though, thanks for the investigation! @Aurelius333 oh that's quite nice! So if we already have that ability, it may not be too much work to update the core to handle a stateful functional component. |
@bjarkihall ... If I understand what you are suggesting, I remembered this had been considered in the past before settling on the approach of using |
@Aurelius333 I didn't notice that, thanks! I'd think the "name" mostly matters when using devtools, so maybe the implementation could use So this is kind of the functionality of Functional Components: const FC = ({counter, increment}) => (props, ctx) => <button onClick={increment}>Counter: {counter.value}</button>
export default {
setup(props, ctx) {
return FC(useCounter()) // don't do this though...
}
} instead of this: const FC = (props, ctx) => {
const {counter, increment} = useCounter();
return <button onClick={increment}>Counter: {counter.value}</button> // this would need to return a render function to work!
}
export default {
setup: FC
} So it actually boils down to this: As I said, there should be clearer notes about this in the docs and a maybe a rule that catches this. EDIT: @aztalbot thanks for the links! I had already written this up before seeing the link where Evan addresses this. |
The type of a component (stateful vs. functional) must be known before hand because the side-effect of calling function Counter(props) {
// the render function itself
return h('div', props.foo)
} vs. function Counter(props) {
// stateful setup logic
return () => h('div', props.foo)
} is completely different. The former is expected to be inside a reactive effect that tracks its dependencies. The latter does not. So it would be too late to determine how this function should be treated based on its return value. So you either force all functional components to return the render function (even for state-less ones, which becomes confusing), or require explicit I think this sort of confusion largely comes from React hooks users, but in Vue 3 the rule of thumb is: functional components are always state-less. Use an object or |
@yyx990803 it makes total sense, but what doesn't make sense is that Also, can't JSX.Elements have a stricter type (instead of accepting any), which enables types for: interface VueElement { ... } // an interface for JSX.Element which resembles VNode
declare global { namespace JSX { interface Element extends VueElement { } } StatelessFC: (props?, ctx?) => VueElement // a stricter render function and StatefulFC: (props?, ctx?) => StatelessFC // a stricter setup function This would hopefully allow us to make these much clearer in both places, since as of now, TS won't detect errors because of VNodeChild is not compatible with JSX.Element, so e.g. Since primitives are supposed to work in a non-JSX context - could the VNodeChild be used for more general components but the more limited/narrow VueElement types in JSX context? |
Version
3.0.0-rc.2
Reproduction link
https://github.com/baryla/vue3-composition-api-functional-component
Steps to reproduce
yarn dev
commandWhat is expected?
I would expect the counter to increment
What is actually happening?
The counter is not incrementing
I'm not exactly sure if this is the desired behaviour but it feels a little strange that this doesn't work.
If it desired and Composition API should only work inside
setup
, maybe it's worth exploring the use inside a functional component and treating the whole function as a setup function? Kind of similar to this RFC where the wholescript
tag is considered a "setup" if it has thesetup
attribute.The text was updated successfully, but these errors were encountered: