Things that I like better in Vue than in React
Tags: vue, react, frontend
Published: 2024-04-08 15:10:26

I’ve been using React for a long time, so when some says “Hey, you should try a different frontend framework”, my answer is always *pffft, but I’m already so efficient =P*. There have been enough cool developments in Vue, though, to make me want to try it. And man was I pleasantly surprised! Vue has a bunch of neat features that make me jealous as React dev. Let's delve into some Vue patterns that stand out against React's approach!

### 1. Installation is Fun

The Vue’s out-of-the-box impressions is light-years ahead — `npm create vue@latest` feels like a *designed experience*, while `npm create-react-app` is just a plain ol’ NPM command. I prefer interactively selecting which packages to install, as done in Vue, over CRA’s process.

On top of that, the packages installed by Vue provide a more complete dev experience than CRA, covering areas like routing, state management, and code formatting.


![Vue’s installation process](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qvj9c44ckp5eehh0gyvi.png)

*Vue’s installation process is awesomely interactive.*

Also, can we talk about how helpful the starter page is when you open `localhost` for your new project?

![Vue starter page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kzz4jc9h3nwtqboc397w.png)

All the Vue examples going forward are using the Composition API from Vue 3, which is most equivalent to functional React.

### **2. Intuitive Reactivity System**

Vue's reactivity system feels more intuitive and less boilerplate-heavy compared to React's state management. Vue handles dependency tracking and updates automatically, making state management straightforward.

**Vue Example**

```html
<template>
  <p>{{ count }}</p>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';

const count = ref(0);

onMounted(() => {
  setInterval(() => {
    count.value++;
  }, 1000);
});

onUnmounted(() => clearInterval(interval));
</script>
```

**React Equivalent**

React requires more boilerplate to achieve similar reactivity, especially for effects and state updates that depend on previous state values. Who else has to look up the subtle differences between `useEffect` , `useMemo` , and `useCallback` every time you want to use them? 🙋🏾‍♂️

```jsx
function Counter() {
  const [count, setCount] = React.useState(0);

  React.useEffect(() => {
    const interval = setInterval(() => {
      setCount(currentCount => currentCount + 1);
    }, 1000);
    return () => clearInterval(interval);
  }, []);

  return <p>{count}</p>;
}
```

### **3. Single-File Components**

Single-file components (SFCs) are definitely a love/hate thing, but I think they are great, especially for small components. In my opinion, encapsulating HTML, JavaScript, and CSS in one file promotes better organization and readability.

**Vue Single File Component**

```html
<script setup lang="ts">
import { defineProps } from 'vue'

defineProps({
  message: String
})
</script>

<template>
  <div class="message-box">
    <p>{{ message }}</p>
  </div>
</template>

<style scoped>
.message-box {
  padding: 20px;
  border: 1px solid #eee;
}
</style>
```

**React Equivalent**

In React, achieving the same encapsulation typically involves separating concerns into different files or adopting CSS-in-JS libraries, which can add complexity.

```jsx
function MessageBox({ message }) {
  return (
    <div className="message-box">
      <p>{message}</p>
    </div>
  );
}
// CSS is managed separately or through CSS-in-JS solutions
```

### **4. Conditional Rendering**

Vue's directive system (**`v-if`**, **`v-for`**, etc.) provides a more readable approach to conditional rendering than React. This advantage becomes more pronounced as your if/else statements become more complicated.

**Vue Example with Directives**

```html
<script setup lang="ts">
import { ref } from 'vue';

const isSubscribed = ref(false);
const isSubscriptionExpiring = ref(true);

// Above properties updated dynamically...
</script>

<template>
  <div>
    <p v-if="!isSubscribed">Please subscribe to access premium features.</p>
    <p v-else-if="isSubscriptionExpiring">Your subscription is expiring soon. Please renew to continue enjoying premium features.</p>
    <p v-else>Welcome back, valued premium user!</p>
  </div>
</template>
```

**React Equivalent**

```jsx
function SubscriptionStatus() {
  const isSubscribed = false;
  const isSubscriptionExpiring = true;
  
  // Above properties updated dynamically...

  return (
    <div>
      {isSubscribed ? (
        isSubscriptionExpiring ? (
          <p>Your subscription is expiring soon. Please renew to continue enjoying premium features.</p>
        ) : (
          <p>Welcome back, valued premium user!</p>
        )
      ) : (
        <p>Please subscribe to access premium features.</p>
      )}
    </div>
  );
}

```

You can of course move the JSX-based logic into a JS function, but the initial pass is easier to understand in Vue.

### **5. Computed Properties**

Vue's computed properties simplify the handling of complex logic that depends on reactive data. They are automatically cached and only re-evaluated when their dependencies change, optimizing performance.

**Vue Example**

```html
<script setup lang="ts">
import { computed, ref } from 'vue'

const price = ref(10)
const quantity = ref(3)
const totalPrice = computed(() => price.value * quantity.value)
</script>

<template>
  <p>Total price: {{ totalPrice }}</p>
</template>
```

**React Equivalent**

In React, similar functionality requires useMemo hook, adding complexity and verbosity to component logic.

```jsx
function TotalPrice() {
  const [price, setPrice] = React.useState(10);
  const [quantity, setQuantity] = React.useState(3);

  const totalPrice = React.useMemo(() => {
    return price * quantity;
  }, [price, quantity]);

  return <p>Total price: {totalPrice}</p>;
}
```

### **6. Streamlined State Management**

Pinia is one of the recommended state management solutions for Vue. Thanks to its tight integration with Vue and thoughtful API, it feels more streamlined than the equivalent code in React + Redux.

**Vue with Pinia**

```jsx
// stores/counter.js

import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => {
    return { count: 0 }
  },
  actions: {
    increment() {
      this.count++
    },
  },
})

// components/MyCounter.vue

<script setup>
import { useCounterStore } from '@/stores/counter'

const counter = useCounterStore()
</script>

<template>
  <button @click="counter.increment">Increment</button>
  <div>Current Count: {{ counter.count }}</div>
</template>
```

**React with Redux**

```jsx
// counterSlice.js

import { createSlice } from '@reduxjs/toolkit'

export const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0,
  },
  reducers: {
    increment: (state) => {
      state.value += 1
    }
  },
})

export const { increment } = counterSlice.actions

export default counterSlice.reducer

// Counter.js

import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { decrement, increment } from './counterSlice'

export function Counter() {
  const count = useSelector((state) => state.counter.value)
  const dispatch = useDispatch()

  return (
    <button onClick={() => dispatch(increment())}>
      Increment
    </button>
    <span>{count}</span>
  )
}
```

### **In Conclusion**

My initial foray into learning Vue has expanded my perspectives on good design patterns and well-built APIs. It’s clear that Vue prioritized developer experience and application performance from the first commit. I think the biggest lesson here is that regardless of your skill level or what tools you have mastered, you can always learn from trying something new!

Is there anything else you like about Vue? Let me know in the comments!

*PS. I share content like this post 2x / week. If you’re interested in frontend, backend, and how to become a better full stack developer,* [subscribe to my newsletter](https://jaydevm.hashnode.dev/newsletter)*!*