VueUse as must-have library for Vue 3
Tags: vue, javascript, webdev, typescript
Published: 2021-09-01 11:26:15

For those who are unfamiliar with this [library](https://vueuse.org/), I advise you to try it, as it can de facto become the standard for use in Vue 3 projects, as, for example, once there was a lodash library for almost any js projects.
Others have probably already checked out all the extensive functionality that it provides. Some have already used it in Vue 2, but not all new features support the old version. The library's arsenal is impressive, there are simple utilities like getting mouse coordinates, and various complex integrations with Firebase, Axios, Cookies, QR, local storage, browser, RxJS, animation, geolocation, extensions for standard Vue hooks, a media player and much more. Evan You himself is noted among the sponsors of the library, which seems to be a good sign. The library receives regular updates, bug fixes, and the community grows. So, it has everything for success.
In this article I will only talk about 4 features, but, of course, all the others need attention.

## `onClickOutside` - clicks outside the element

I'm sure you can handle the installation of the library by yourself, so let's go straight to the interesting features. To warm up, consider a simple hook that tracks clicks outside a given element - [onClickOutside](https://vueuse.org/core/onClickOutside/). There are many packages that provide this functionality, and almost everyone has probably written that function by themselves. Usually it is implemented by adding a custom Vue directive to the desired element, for example `v-clickOutside`, but the use of a hook is unusual.
I used this hook in my todo app, in the `ToDoItem.vue` component:

```html
<template>
  <li ref="todoItem">
    <input type="checkbox" />
    <span v-if="!editable" @click="editable = !editable">
      {{ todo.text ? todo.text : "Click to edit Todo" }}
    </span>
    <input
      v-else
      type="text"
      :value="todo.text"
      @keyup.enter="editable = !editable"
    />
  </li>
</template>

<script lang="ts">
  import { defineComponent, PropType, ref } from "vue"
  import ToDo from "@/models/ToDoModel"
  import { onClickOutside } from "@vueuse/core"

  export default defineComponent({
    name: "TodoItem",
    props: {
      todo: {
        type: Object as PropType<ToDo>,
        required: true
      }
    },
    setup() {
      const todoItem = ref(null)
      const editable = ref(false)

      onClickOutside(todoItem, () => {
        editable.value = false
      })

      return { todoItem, editable }
    }
  })
</script>
```

I removed the extra code to avoid distraction, but the component is still large enough. Pay attention to the code inside the setup hook, first we create an empty todoItem link, which we hang on the desired element in the template, and then we pass the first parameter to the `onClickOutside` hook, and the second parameter is a callback with the actions we need. When you click on the span tag, it will be replaced with an input tag, and if you click outside the li tag with the `ref="todoItem"` attribute, then the input will be replaced with a span tag.

## `useStorage` and `createGlobalState` - reactive local storage

The next function I'll talk about is useStorage. This function allows data to be stored in `Window.localStorage`. It is convenient to use it in conjunction with `createGlobalState`, which is used to create a global storage. Now the data will be saved, updated and deleted automatically, and will not disappear after the page is reloaded. Below is the example of using these functions:

```javascript
// @/store/index.ts
import { createGlobalState, useStorage } from '@vueuse/core'
import Note from '@/models/NoteModel'

// state
export const useGlobalNotes = createGlobalState(
  () => useStorage('my-notes', [] as Note[]),
)

// actions

const notes = useGlobalNotes() // for local use

export const addNote = function (note) {
  notes.value.push(note)
}

export const deleteGlobalNote = function (noteId: number) {
  notes.value = notes.value.filter(note => note.id != noteId)
}
```

The first parameter of the useStorage function accepts a key under which it will save your data in `localStorage`, and the second is initial value. createGlobalState creates a wrapper function to pass state to components. By calling this function (in our case it is `useGlobalNotes()`) in Vue-components or right here in this file, we will get a reactive list of notes. The notes array can be used as usual, remembering that since this is a proxy object, and the list itself is stored in notes.value. No need to add `.value` to markup templates in components.
For comparison, it's also helpful to see the useStorage example from the authors of the library. The difference is that in setup, you need to work with reactive storage not directly, but through its value property. In the html template, everything is as usual.

## `useRefHistory` - history of changes

`useRefHistory` is a hook that will record the history of data changes and provide undo/redo functionality. I used it to create the Undo and Redo buttons on the note editing page. First, I created a reactive variable using ref. Let's take a closer look at the code:

```html
<!--  Note.vue -->
<template>
  <!--  ...  -->
  <div>
    <button type="button" @click="undo" :disabled="!canUndo">Undo</button>
    <button type="button" @click="redo" :disabled="!canRedo">Redo</button>
  </div>
  <!--  ... -->
</template>

<script lang="ts">
    import { defineComponent } from "vue"
    import { useRefHistory } from "@vueuse/core"
    import ToDo from '@/models/ToDoModel'

    export default defineComponent({
      setup() {
        const note = ref({
                title: "",
                todos: [] as ToDo[]
              })
    	const {
      		undo,
      		redo,
      		canUndo,
      		canRedo,
      		clear
    		} = useRefHistory(note, { deep: true })

    	const updateTitle = (title: string) => {
      		note.value.title = title
    	}

    	const addNewTodo = () => {
      		note.value.todos.push({} as ToDo)
    	}

    	const onRemoveTodo = (index: number) => {
      		note.value.todos.splice(index, 1)
    	}

    	return {
      		note,
      		addNewTodo,
      		onRemoveTodo,
      		updateTitle,
      		undo,
      		redo,
      		canUndo,
      		canRedo,
      		clear
    	}
    },
  })
</script>
```

We create a reactive variable using ref, pass it to the useRefHistory hook, denote deep: true in the hook parameters, for nested objects. Using destructuring assignment from useRefHistory we get `history`, `undo`, `redo`, `canUndo`, `canRedo` and `clear`. The `canUndo` and `canRedo` properties hang on the disabled attributes in buttons. `clear` - needed to clear history after finishing editing records. The `useManualRefHistory` hook does almost the same thing, but saving to history occurs only when the `commit()` command is called.

## Conclusion

I have covered only 4 functions from the large arsenal of VueUse tools for Vue 3 development. For a more in-depth study, I advise you to visit the [site](https://vueuse.org/) of this wonderful library. While the documentation could still be improved, it is regularly being updated as well as the library itself.
The complete code of my polygon, where I tested this library, can be viewed here.