Stateful logic reuse in Vue.js

When building applications, we are often faced with scenarios where we need to reuse some logic. Say you want to format your dates and time across your application

· 3 min read
Jackson Obere

Jackson Obere

Software Engineer

topics

What is a Composable?

A composable is a function in Vue.JS composition API that lets you reuse stateful logic.

When building applications, we are often faced with scenarios where we need to reuse some logic. Say you want to format your dates and time across your application, you will create a function that does this. You have the option of writing the same function in all the components that need it or extract a reusable function that will do it, the created function encapsulates stateless logic. At the point of its calling it takes in a specific input and returns output. In contrary stateful logic involves state that changes overtime.

Mouse tracking

A simple example would be a mouse tracking function. If you wanted to display the position of a mouse each time it moves using the composition API, we would write out the functionality in a single component like this.

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

const x = ref(0)
const y = ref(0)

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

onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
</script>

<template>Mouse position is at: {{ x }}, {{ y }}</template>

But what if we wanted to reuse the same logic in multiple components? We can extract the logic into an eternal file i.e. as a composable function. By convention composable function names start with use.

// mouse.js
import { ref, onMounted, onUnmounted } from 'vue'


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


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


  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))


  return { x, y }
}

And this is how you use it in a component

<script setup>
import { useMouse } from './mouse.js'

const { x, y } = useMouse()
</script>

<template>Mouse position is at: {{ x }}, {{ y }}</template>

As evident, the core logic has not changed, all we had to do was move it into an external function and return the state that should be exposed. Just like inside a component, you can use the full range of Composition API functions in composables. The same useMouse() functionality can now be used in any components.

Conventions and Best Practices

Following conventions and best practices is essential for maintaining readability, consistency, and scalability within a project. Conventions help in creating cohesive coding stylez across all developers reducing conflict. Here are some of the best practices while using composables.

Naming

Composables are named using camelCase and they begin with the word use.

Input Arguments

Composables can accept ref or getter arguments even if it does not rely on them for reactivity.

Return Values

The recommended convention is for composables to always return a plain, non-reactive object containing multiple refs. This allows it to be destructured in components while retaining reactivity:

Returning a reactive object from a composable will cause such destructures to loose the reactivity connection to the state inside the composable, while the refs will retain that connection.

Usage Restrictions

Composables should only be called in <script setup> or the setup() hook. They should can also be called synchronously in these contexts. In some cases, you can also call them in lifecycle hooks like onMounted().

Hope you found this useful.

Peace!

share

Jackson Obere

I enjoy working with front-end technologies like Vue, React, and Vanilla JavaScript, with some Python on the side. I love building interactive web experiences and breaking down tricky concepts to make them easier to understand. I'm always curious and enjoy learning new things in the ever-evolving tech space.