Create your own custom Vue Hooks: useInterval

Ankur Singhal
4 min readFeb 13, 2023

In this article, I will show you the process to create a custom Vue composable hook that can be used across any app or component.

I will demonstrate that by creating a hook called useIntervalwhich allow s us to manage intervals easily.

Using this approach you can create your own custom Vue Composables. Before jumping to the composable API, please look at the below code to understand how Vue2 component is different from Vue3 composable API.

Vue3 hooks vs Vue2 component spec:-

// Vue2 Counter component
const Counter = {
data() {
return { count: 0 }
},
computed: {
countToString() { return this.count.toString(); }
},
watch: {
count() {
// fetchData
}
},
methods: {
inc() { this.count++ },
dec() { this.count-- },
reset() { this.count = 0 },
},
mounted() {
// add document event listeners
},
destroyed() {
// remove document event listeners
}
};

// Vue3 component component using composable api
const Counter = {
setup() {
// state which we define in the `data` function of the component spec
const count = ref(0)

// all lifecycle methods
onMounted(() => // add document event listeners)
onUnmounted(() => // remove document event listeners)

// watchers
watch(count, (newVal) => // fetch data)

// computed properties
const countToString = computed(() => count.value.toString());

const inc = () => count.value++;
const dec = () => count.value--;
const reset = () => count.value = 0;

return {
count,
countToString
inc,
dec,
reset,
}
}
}

To create our useInterval hook we will set up an interval using setInterval using the onMounted hook and clear it onUnmounted. Also, we will ask for two parameters for this hook, one will be the callback and another the time in milliseconds.

import { onMounted, onUnmounted } from 'vue';

function useInterval(callback, time = 1000) {
let timer = null;

onMounted(() => timer = setInterval(callback, time));
onUnmounted(() => clearInterval(timer));
}

That will work but it’s a very poor design, what if the developer wants to stop the interval during any point in time and then restart it, which should give the developer the power to accomplish that? Let’s add start and stop utilities to our hook.

import { onMounted, onUnmounted } from 'vue';

function useInterval(callback, time = 1000) {
let timer = null;

function start() {
timer = setInterval(callback, time);
}

function stop() {
if (timer !== null) {
clearInterval(timer);
timer = null;
}
}

onMounted(start);
onUnmounted(stop);

return { start, stop };
}

As you can see we returned our start and stop actions to manage the interval. Since the developer can call start again and again, we should clear the interval just in case. Let’s stop the interval before creating a new one.

import { onMounted, onUnmounted } from 'vue';

function useInterval(callback, time = 1000) {
let timer = null;

function start() {
stop();
timer = setInterval(callback, time);
}

function stop() {
if (timer !== null) {
clearInterval(timer);
timer = null;
}
}

onMounted(start);
onUnmounted(stop);

return { start, stop };
}

This will make sure the earlier intervals are cleared before creating a new one.

That’s it, useInterval now can be used anywhere across all our components in our app without writing any of the code again and again. Let’s try using it in an example app. Let’s display just the current time in the template for every second.

Above is the Live Preview of the Demo. Let’s see the App code below.

<template>
<div id="app">
{{ timeString }}
</div>
</template>

<script>
import { ref, computed } from 'vue';
import { useInterval } from './useInterval';

export default {
name: 'App',
setup() {
const time = ref(new Date());

function intervalHandler() {
time.value = new Date();
}

useInterval(intervalHandler);

const timeString = computed(() => time.value.toTimeString());

return {
timeString,
};
},
};
</script>

Here we use our hook useInterval which will keep on calling handler function for every 1000 milliseconds passed and in the handler we just set our reactive time property to the current date. Also notice we use computed to calculate the current time using toTimeString on the Date object.

Also, we can further refactor our Date updates handling to its own hook, let’s name it useDateInterval:-

<template>
<div id="app">
{{ timeString }}
</div>
</template>

<script>
import { ref, computed } from 'vue';
import { useInterval } from './useInterval';

function useDateInterval(interval = 1000) {
const time = ref(new Date());

function intervalHandler() {
time.value = new Date();
}

useInterval(intervalHandler, interval);

return time;
}

export default {
name: 'App',
setup() {
const time = useDateInterval();
const timeString = computed(() => time.value.toTimeString());

return {
timeString,
};
},
};
</script>

So easy! You can use these native composable API hooks to create your own custom composable hooks and more and more 🚀🚀🚀.

I will be writing more articles on Vue Composable API, so stay tuned and follow. 👍🏻

--

--