Improving Reactivity with VueUse

VueUse is a library of over 200 utility functions that can be used to interact with a range of APIs including those for the browser, state, network, animation, and time. These functions allow developers to easily add reactive capabilities to their Vue.js projects, helping them to build powerful and responsive user interfaces with ease.

One of the most exciting features of VueUse is its support for direct manipulation of reactive data. This means that developers can easily update data in real-time, without the need for complex and error-prone code. This makes it easy to build applications that can react to changes in data and update the user interface accordingly, without the need for manual intervention.

This article seeks to explore some of the VueUse utilities to help us improve reactivity in our Vue 3 application.

let’s get Started!

Installation

To get started, let’s setup our Vue.js development environment. We will be using Vue.js 3 and the composition API for this for tutorial so if you are not familiar with the composition API you are in luck. An extensive course from Vue School is available to help you get started and gain massive confidence using the composition API.

//create vue project with Vite
npm create vite@latest reactivity-project -- --template vue

cd reactivity-project

//install dependencies
npm install

//Start dev server
npm run dev

Now our Vue.js project is up and running. Let’s install the VueUse library by running the following command:

npm install vueuse

Screenshot 2022-12-14 at 9.36.45 AM.png

With VueUse installed, we can now start exploring some VueUse utilities.

refDebounced

Using the refDebounced function, you can create a debounced version of a ref that will only update its value after a certain amount of time has passed without any new changes to the ref's value. This can be useful in cases where you want to delay the update of a ref's value to avoid unnecessary updates, also useful in cases when you are making an ajax request (like in a search input) or to improve performance.

Now let’s see how we can use refDebounced in an example.

<template>
  <div>
    <input type="text" v-model="myText" />
    <p>{{ debounced }}</p>
  </div>
</template>

<script setup>
import { ref } from "vue";
import { refDebounced } from "@vueuse/core";
const myText = ref("hello");
const debounced = refDebounced(myText, 1000);
</script>

Now, whenever the value of myText changes, the value of debounced will not be updated until at least 1 second has passed without any further changes to the value of myText.

Screen Recording 2022-12-19 at 12.18.18 PM.gif

useRefHistory

The "useRefHistory" utility is a VueUse composable that allows you to keep track of the history of a ref's value. It provides a way to access the previous values of a ref, as well as the current value.

Using the useRefHistory function, you can easily implement features such as undo/redo or time travel debugging in your Vue.js application.

let’s try a basic example

<template>
  <div>
    <form action="#" @submit.prevent="changeText()">
      <input type="text" v-model="inputText" /> <button>Submit</button>
    </form>
    <p>{{ myText }}</p>
    <button @click="undo">Undo</button>
    <button @click="redo">Redo</button>
  </div>
</template>

<script setup>
import { ref } from "vue";
import { useRefHistory } from "@vueuse/core";
const myText = ref("hello");
const inputText = ref("");
const { history, undo, redo } = useRefHistory(myText);

const changeText = () => {
  myText.value = inputText.value;
};

</script>

In our above example we have a basic form to change our hello text to any text we input in our form. We then link our myText state to our useRefHistory function which is able to track the history of our myText state. We include a redo button and an undo button to time travel across our history to view past values.

Screen Recording 2022-12-19 at 12.13.45 PM.gif

refAutoReset

Using the refAutoReset composable, you can create refs that automatically reset to a default value after a period of inactivity, which can be useful in cases where you want to prevent stale data from being displayed or to reset form inputs after a certain amount of time has passed.

Let’s jump into an example.

<template>
  <div>
    <form action="#" @submit.prevent="changeText()">
      <input type="text" v-model="myText" /> <button>Submit</button>
    </form>
    <p>{{ message }}</p>
  </div>
</template>

<script setup>
import { ref } from "vue";
import { refAutoReset } from "@vueuse/core";
const myText = ref("");
const message = refAutoReset("default message", 5000);

const changeText = () => {
  message.value = myText.value;
};
</script>

Now, whenever the value of message changes, the countdown to resetting the value to 0 will be reset. If 5 seconds passes without any changes to the value of message, the value will be reset to default message.

Screen Recording 2022-12-19 at 11.53.31 AM.gif

refDefault

With the refDefault composable, you can create refs that have a default value to be used when the ref's value is undefined, which can be useful in cases where you want to ensure that a ref always has a value or to provide a default value for form inputs.

<template>
  <div>
    <input type="text" v-model="myText" />
    <p>{{ myText }}</p>
  </div>
</template>

<script setup>
import { refDefault } from "@vueuse/core";
const myText = refDefault("hello");
</script>

In our example, whenever our myText value is set to undefined it switches to hello.

ComputedEager

Sometimes using a computed property may be a wrong tool as its lazy evaluation can degrade performance. Let’s take a look at a perfect example

<template>
  <div>
    <p>{{ counter }}</p>
    <button @click="counter++">Increase</button>
    <p>Greater than 100: {{checkCount}} </p>
  </div>
</template>

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

const counter = ref(0)

const checkCount = computed(() => {
    return this.counter >  5
})
</script>

With our example we notice that for checkCount to be true we will have to click our increase button 6 times. We may think that our checkCount state only renders twice that is initially on page load and when counter.value gets to 6 but that is not true. For every time we run our computed function our state re-renders which means our checkCount state renders 6 times, sometimes affecting performance.

Screen Recording 2022-12-19 at 12.27.05 PM.gif

This is where computedEager comes to our rescue. ComputedEager only re-renders our updated state when it needs to.

Now let’s improve our example with computedEager.

<template>
  <div>
    <p>{{ counter }}</p>
    <button @click="counter--">Undo</button>
    <p>Greater than 5: {{checkCount}} </p>
  </div>
</template>

<script setup>
import { ref } from "vue";
import { computedEager } from '@vueuse/core'

const counter = ref(0)

const checkCount = computedEager(() => {
  return this.counter >  5
})

watchEffect(() => {
  console.log(checkCount.value);
  render.value++;
});
</script>

Now our checkCount only re-renders only when counter is greater than 5.

Screen Recording 2022-12-19 at 2.14.21 PM.gif

Conclusion

In summary, VueUse is a collection of utility functions that can significantly enhance the reactivity of your Vue.js projects. There are many more functions available beyond the ones mentioned here, so be sure to explore the official documentation to learn about all of the options. Additionally, if you want a hands-on guide to using VueUse, VueUse for Everyone online course by Vue School is an excellent resource to get started.

With VueUse, you can easily track and manage your application state, create reactive computed properties, and perform complex operations with minimal code. They are a vital component of the Vue.js ecosystem and can greatly improve the efficiency and maintainability of your projects.