Creating a debounced input component using Vue Composition API
Tags: vue, webdev, frontend, javascript
Published: 2020-04-25 15:24:00

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
```javascript
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')

```

4. Create a new component in components folder called `DebouncedInput.vue` and copy the following code
```vue
<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

```javascript
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).

```javascript
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.
```javascript
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.

```javascript
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
```javascript
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 
```vue
<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
- https://www.vuemastery.com/pdf/Vue-3-Cheat-Sheet.pdf
- https://dev.to/gabe_ragland/debouncing-with-react-hooks-jci
- https://www.vuemastery.com/blog/vue-3-start-using-it-today/