With the Vue 3 Composition API, you can write functional components. It's possible also with React, but React has different templating: JSX. What happens if we write Vue functional components with JSX templates? Are they similar to React functional components?
Let's look at how both frameworks functional components work and how similar/different they're through. The component we'll use for this purpose is a counter, which counts clicks on a button. Additionally it receives a limit parameter: when this limit is reached the component notifies its parent.
We'll create the React component first and then look at the Vue equivalent.
React
import { useState, useEffect } from 'react';
export const Counter = ({ limit, onLimit }) => {
const [count, setCount] = useState(0);
const handler = () => setCount(count + 1);
useEffect(
() => (count >= limit) ? onLimit() : null,
[count]
);
return <button type="button" onClick={handler}>
Count: {count}
</button>;
};
React requires a plain Javascript function that returns a JSX template to create a component. This function reruns whenever the component's state changes. You can create such state with the useState
method. State variables are plain Javascript constructs that persist value between reruns. Every other variable is lost between state changes. You can test it with a console.log
statement at the top of the function.
The component has a limit and a method which can be used to notify the parent component. We want to check the current value whenever it is incremented. The useEffect
function serves as a checker and runs the callback whenever the dependencies in the second argument change.
In a nutshell: React component is a plain function with plain Javascript state values that reruns on every state change and returns JSX.
Vue
import { defineComponent, ref, watchEffect } from 'vue';
export const Counter = defineComponent({
props: ['limit', 'onLimit'],
setup(props) {
const count = ref(0);
const handler = () => count.value++;
watchEffect(
() => (count.value >= props.limit) ? props.onLimit() : null
);
return () => <button type="button" onClick={handler}>
Count: {count.value}
</button>;
}
});
The plain function equivalent in Vue is the setup
method within the component object. The setup
method also receives props
as an input parameter, but instead of JSX, it returns a function that returns JSX. You may wonder why.
The reason is because the setup
function only runs once and only the returned function runs on state change. If the setup
function only runs once, how can Vue detect changes? The trick lies in Vue's reactivity system. The ref
function wraps the original value inside a Javascript Proxy
object. Every modification runs through this proxy which notifies Vue to rerender that component. If we modify the original value directly that change will be ignored by the framework.
The limit and notifier function come as a function parameter, but in Vue we haven't used destructuring. The reason is that props
is also a Proxy object and if we destructure it, we lose its reactivity (if it changes, nothing would happen). To check value changes, we have to use the useEffect
function. In contrary to React, we don't have to define the watched dependencies, Vue does it automatically as it knows about which state variables (Proxies
) we use inside the callback.
For Vue developers using a function instead of an event to notify the parent might be unusual. Some say it's an anti-pattern in Vue, but to make it as close to React as possible I've chosen this way.
Summary
Both framework can create a component with a single function. The Vue functional component is a function with state values wrapped inside Proxies that only runs once and only the returned function reruns and returns JSX. The React functional component is a function with state values as plain Javascript constructs that reruns on every state change and returns JSX directly.
The difference lies in the way how each framework solves the problem of reactivity: React is the stateless reference comparing solution, Vue is the stateful Proxy based solution.
It was an interesting and fun experiment to try to write the same component in different frameworks with similar approach as identically as possible. I hope you find it also interesting. You can also give it a try in my Vue 3 Vite playground.
Top comments (8)
I Love Vue because i hate JSX. :)
I tried this last week myself
Is there a way to get type/variable-name checking within the (j,t)sx part?
So in this example I misspelled
label
in therender
function, but TSX doesn't provide me an error for that :(Seems I need at least something like this
Then
this.lable
is an errorThis is beautiful, such a huge improvement – no longer will I suffer from magic stringly-typed variables appearing in templates <3.
JSX or the functional api?
Can you. elaborate on what is worse? I feel JSX as a different approach, not a better/worse one - different people different tastes.
yo creo que si quieres jsx seria mejor utilizar react js por su sintaxis y codigo menos engoroso , hasta el momento funcional component no acepta estado...
aunque yo utilizo Vue me siento mas familiarizado con Template y con composition api
Wish Dev.to had a downvote button. Stop bringing your React anti-patterns to other ecosystems. If you want to type React code, use React, not Vue.
You can extract the business logic as a plain function. The JSX templating is just for the experiment. JSX in many cases suffers performance penalty compared to Vue templates.