DEV Community

Cover image for Creating a debounced input component using Vue Composition API
Heru Joko Priyo Utomo
Heru Joko Priyo Utomo

Posted on • Edited on

Creating a debounced input component using Vue Composition API

Few weeks ago I was browsing up dev.to to look up some references for custom hook in react, and found this great article https://dev.to/gabe_ragland/debouncing-with-react-hooks-jci . I also heard that vue 3 is coming with similar API with react hooks called compostion API and that part already available in https://www.npmjs.com/package/@vue/composition-api for Vue 2. Lets try to create similar thing there using vue new feature.

  1. Create a new vue project (you can look up to google on how to do this)
  2. Install https://www.npmjs.com/package/@vue/composition-api by running npm i @vue/composition-api -S
  3. Run the project with npm run serve
  4. In the main.js we need to info vue to use the composition api plugin, otherwise it won't compile the new syntax correctly
import Vue from 'vue'
import App from './App.vue'
import VueCompositionApi from "@vue/composition-api";

Vue.use(VueCompositionApi);
Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

  1. Create a new component in components folder called DebouncedInput.vue and copy the following code
<template>
  <div>
    <input :value="displayValue" @input="debounceListener" placeholder="search here" />
    <p>deb : {{ debouncedValue }}</p>
  </div>
</template>

<script>
import { ref } from "@vue/composition-api";

export default {
  setup() {
    let timeoutRef = null;
    const displayValue = ref("");
    const debouncedValue = ref("");

    const debounceListener = e => {
      if (timeoutRef !== null) {
        clearTimeout(timeoutRef);
      }

      displayValue.value = e.target.value;
      timeoutRef = setTimeout(() => {
        debouncedValue.value = e.target.value;
      }, 800);
    };

    return { debouncedValue, displayValue, debounceListener };
  }
};
</script>

Ok, what was that huh? seems strange for usual vue file. its the new composition API syntax which allow us to write our logic code more modular / functional you can look up for more info here https://composition-api.vuejs.org/. Okay now we break down few things about code above

import { ref } from "@vue/composition-api";

in this line we import our ref function from the plugin, basically, it will turn our variables into a reactive variable (like useState in react hooks).

let timeoutRef = null;
const displayValue = ref("");
const debouncedValue = ref("");

here is how we create the reactive data. timeourRef is a helper variable that we will use to maintain our timeout and won't be used in view, so no need to be reactive.

const debounceListener = e => {
  if (timeoutRef !== null) {
    clearTimeout(timeoutRef);
  }

  displayValue.value = e.target.value;
  timeoutRef = setTimeout(() => {
    debouncedValue.value = e.target.value;
  }, 800);
};

this is the main function, basically it just delay the process to set the debouncedValue and clear the timeout if there is a previous timeout but new event input is already typed.

return { debouncedValue, displayValue, debounceListener };

finally we return all the reactive variable and the function.

Now we have a usable component with new composition api. but this is the same behaviour like previous vue syntax that we need to share the logic and the view as a component. What if like we need to just reuse the logic only? this is where this new syntax comes in handy.

Reusable logic

Now create a new file called useDebounce.js inside src/composed folder. then use the following code

import { ref } from "@vue/composition-api";

export default function (timeoutCount = 800) {
    let timeoutRef = null;
    const displayValue = ref("");
    const debouncedValue = ref("");

    const debounceListener = e => {
        if (timeoutRef !== null) {
            clearTimeout(timeoutRef);
        }

        displayValue.value = e.target.value;
        timeoutRef = setTimeout(() => {
            debouncedValue.value = e.target.value;
        }, timeoutCount);
    };

    return { debouncedValue, displayValue, debounceListener };
}

basically it is almost like the prev component but we wrap it inside a function (oh maybe this is called functional approach?) then we pass a parameter to easily customize how long we wanna wait for the timeout. Then in our previous component, we can update the code to be like

<template>
  <div>
    <input :value="displayValue" @input="debounceListener" placeholder="search here" />
    <p>deb : {{ debouncedValue }}</p>
  </div>
</template>

<script>
import useDebounce from "../composed/useDebounce";

export default {
  setup() {
    return {
      ...useDebounce(2000)
    };
  }
};
</script>

wow so much cleaner, and we can mix and match other logic inside our component.

Conclusion

Reusable code is one of the signs of good code, as more and more duplicated code lives in our codebase will make our codebase harder to maintain. Really look up for this composition api to be ready to use in vue 3.

please let me know your thoughts or if have other possible cool use cases to use this cool feature. If you find my explanation hard to follow can also look up to the code in my github https://github.com/heruujoko/use-debounce-vue

Reference

Top comments (0)