DEV Community

alessiochiffi
alessiochiffi

Posted on • Edited on

Vue Composition API and React Hooks comparison

With the release of Vue 3 we can now make use of the new Composition API, a set of additive, function-based APIs that allow flexible composition of component logic.

Using Composition API will give you multiple benefits like extraction and reuse of logic between components, Typescript support, local and global state management (you can read more here about State Management with Composition API)

Here is a brief comparison between React and Vue hooks

Basic Hooks

useState

React:

const [currentState, setCurrentState] = useState("");
Enter fullscreen mode Exit fullscreen mode

Vue

const currentState = ref("");
Enter fullscreen mode Exit fullscreen mode

With Vue 3 we have ref (used for primitives) and reactive (used for objects). Vue creates a reactive object where we can set/get the value.

// to set a value
currentState.value = "New value"

// to read the value
currentState.value
Enter fullscreen mode Exit fullscreen mode

It is also possible to share the state with other components using provide and inject (more info here)

useEffect

React:

useEffect(() => {
 console.log(`${currentState}`);
});
Enter fullscreen mode Exit fullscreen mode

Vue

watchEffect(() => console.log(currentState))

// or watch
watch(currentState, (currentState, prevCurrentState) => {
  console.log(currentState, prevCurrentState)
})

Enter fullscreen mode Exit fullscreen mode

Vue 3 introduces watchEffect, useful when you want to keep track of multiple source changes and when you don't need to know the old values.
You can also use watch to track a single source lazily. The main difference between the two is that watchEffect runs immediately while watch runs lazily.
watchEffect also doesn't require separating the watched data source and the side effect callback.

Computed and useMemo

In Vue 3, computed is a function that returns a reactive value based on one or more reactive sources. It can be used to derive a new value from existing reactive data without triggering unnecessary re-renders. When any of the reactive sources change, the computed value is automatically updated.

In React, useMemo is a hook that takes a function and an array of dependencies, and returns a memoized value that is only recalculated when one of the dependencies changes. It can be used to improve the performance of a component by avoiding expensive calculations on every render.

Vue

<template>
  <div>
    <p>Sum of all numbers: {{ sum }}</p>
    <p>Numbers greater than {{ threshold }}:</p>
    <ul>
      <li v-for="num in filteredNumbers" :key="num">{{ num }}</li>
    </ul>
    <input type="number" v-model.number="threshold" />
  </div>
</template>

<script setup>
import { ref, computed } from 'vue';

const numbers = ref([1, 2, 3, 4, 5]);
const threshold = ref(3);

const sum = computed(() => {
  return numbers.value.reduce((acc, num) => acc + num, 0);
});

const filteredNumbers = computed(() => {
  return numbers.value.filter(num => num > threshold.value);
});
</script>
Enter fullscreen mode Exit fullscreen mode

React

import { useState, useMemo } from 'react';

function NumberList({ numbers }) {
  const [threshold, setThreshold] = useState(10);

  const sum = useMemo(() => {
    return numbers.reduce((acc, num) => acc + num, 0);
  }, [numbers]);

  const filteredNumbers = useMemo(() => {
    return numbers.filter(num => num > threshold);
  }, [numbers, threshold]);

  return (
    <div>
      <p>Sum of all numbers: {sum}</p>
      <p>Numbers greater than {threshold}:</p>
      <ul>
        {filteredNumbers.map(num => <li key={num}>{num}</li>)}
      </ul>
      <input type="number" value={threshold} onChange={e => setThreshold(parseInt(e.target.value))} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Custom Hooks / Extraction and Reuse of logic

We can easily create custom hooks. Here is an example of a function returning the mouse position

React

import { useEffect, useState } from "react";

export const useMousePosition = () => {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    const update = e => setPosition({ x: e.clientX, y: e.clientY });
    window.addEventListener("mousemove", update);

    return () => {
      window.removeEventListener("mousemove", update);
    };
  }, []);

  return position;
};
Enter fullscreen mode Exit fullscreen mode

Vue

import { ref, onMounted, onUnmounted } from 'vue'

export function useMousePosition() {
  const x = ref(0)
  const y = ref(0)

  function update(e) {
    x.value = e.pageX
    y.value = e.pageY
  }

  onMounted(() => {
    window.addEventListener('mousemove', update)
  })

  onUnmounted(() => {
    window.removeEventListener('mousemove', update)
  })

  return { x, y }
}
Enter fullscreen mode Exit fullscreen mode

That's all for now! I will try to keep updated this post with new examples.

Top comments (2)

Collapse
 
adricq profile image
Adrian Capote

Great post!!!

Collapse
 
tarekhassan410 profile image
Tarek Hassan

What a good and brief post! Thank you.