DEV Community

Cover image for Write Vue like you write React
Gábor Soós
Gábor Soós

Posted on • Edited on • Originally published at sonicoder.com

Write Vue like you write React

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>;
};
Enter fullscreen mode Exit fullscreen mode

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>;
  }
});
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
emretoprak profile image
Emre Toprak

I Love Vue because i hate JSX. :)

Collapse
 
shinigami92 profile image
Shinigami • Edited

I tried this last week myself

Is there a way to get type/variable-name checking within the (j,t)sx part?

import { defineComponent } from '@vue/composition-api';
import type { VNode } from 'vue';

export default defineComponent({
  name: 'ComponentName',
  props: { label: String },
  render(): VNode {
    return <div>{ this.lable }</div>;
  }
});
Enter fullscreen mode Exit fullscreen mode

So in this example I misspelled label in the render function, but TSX doesn't provide me an error for that :(


Collapse
 
shinigami92 profile image
Shinigami

Seems I need at least something like this

interface Data {
  label: string;
}
export default defineComponent<unknown, Data, Data>( //...
Enter fullscreen mode Exit fullscreen mode

Then this.lable is an error

Collapse
 
leefreemanxyz profile image
Lee Freeman

This is beautiful, such a huge improvement – no longer will I suffer from magic stringly-typed variables appearing in templates <3.

Collapse
 
sonicoder profile image
Gábor Soós

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.

Collapse
 
ottoajanel_4 profile image
OttoAjanel

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

Collapse
 
ozzythegiant profile image
Oziel Perez

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.

 
sonicoder profile image
Gábor Soós

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.