Debouncing/throttling a method using Vue Class Component Syntax
Asked Answered
J

3

5

I'm working on a component which queries an external API when the text in a search bar changes, and I'm trying to debounce that query so it can only execute every 2 seconds. I'm trying to use lodash's debounce function to do that, and have found multiple blog posts and SO questions about using it with Vue components, but things are complicated because I am using Typescript and the Vue Class Component Syntax (https://class-component.vuejs.org/). And to be honest I'm quite new to both of those.

I found a blog post outlining how to do this with object-based Vue component syntax, but it doesn't apply to the Class Component Syntax. The object-based syntax allows you to wrap your methods inside a _.debounce, like this:

export default {
  methods: {
    throttledMethod: _.debounce(() => {
      console.log('I only get fired once every two seconds, max!')
    }, 2000)
  }
}

Is there a way to do something similar with the Vue Class Component syntax?

Here are the relevant sections of my code (without any attempts at debouncing):

<template>
  <input
    v-model="searchQuery"
    @keydown="doSearch"
  >
</template>

<script lang="ts">
import axios from 'axios';
import _ from 'lodash';
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class FooSearch extends Vue {
  // data
  searchQuery = '';
  results = [];

  // methods
  async doSearch() {
    try {
      const response = await axios.get('https://api.example.org/search', {
        params: {
          query: this.searchQuery,
        }
      });

      this.results = response.data.results;
    } catch(error) {
      console.log('error');
      console.log(error);
    }
  }
</script>
Jennet answered 14/3, 2020 at 20:0 Comment(1)
#57790268Galitea
S
5

It has been discussed here.

Basically, you need to define your base function (like you did with doSearch) and then define the new debounced function:

public doSearchDebounced = _.debounce(this.doSearch, 2000)

Now yu simply need to call doSearchDebounced instead of doSearch

Smokeproof answered 25/6, 2020 at 10:21 Comment(0)
S
2

Although the above has been answered, I think we should make good use of createDecorator in vue-class-component and integrate lodash to simplify the use of Debounce and Throttle.

Create decorator.ts


import { createDecorator } from "vue-class-component";
import _ from "lodash";

export const Debounce = (waitMs: number) =>
  createDecorator((options, key) => {
    if (options.methods && options.methods[key]) {
      const originalMethod = options.methods[key];
      const debounceMethod = _.debounce(originalMethod, waitMs, {
        leading: false,
        trailing: true,
      });

      options.methods[key] = async function (...args: any) {
        await debounceMethod.apply(this, args);
      };
    }
  });

export const Throttle = (waitMs: number) =>
  createDecorator((options, key) => {
    if (options.methods && options.methods[key]) {
      const originalMethod = options.methods[key];
      const throttleMethod = _.throttle(originalMethod, waitMs, {
        leading: true,
        trailing: false,
      });

      options.methods[key] = async function (...args: any) {
        await throttleMethod.apply(this, args);
      };
    }
  });

Refactor code and use decorator.

<template>
  <input
    v-model="searchQuery"
    @keydown="doSearch"
  >
</template>

<script lang="ts">
import axios from 'axios';
import { Component, Vue } from 'vue-property-decorator';
import { Debounce } from "@/decorator";

@Component
export default class FooSearch extends Vue {
  // data
  searchQuery = '';
  results = [];

  // add the Debounce annotation
  @Debounce(1500)
  async doSearch() {
    try {
      const response = await axios.get('https://api.example.org/search', {
        params: {
          query: this.searchQuery,
        }
      });

      this.results = response.data.results;
    } catch(error) {
      console.log('error');
      console.log(error);
    }
  }
</script>
Specialty answered 30/11, 2021 at 1:40 Comment(4)
Nice, but how is this superior to @George Wayne's answer of August 2020?Cole
@LeeGoddard If you only use debounce method seems use decko it's enough. But if you want to customize method like Throttle orLogging you can use this example.Specialty
The question is about debounce?Cole
@LeeGoddard I think the question is used lodash debounce and vue-class-components to integration. It's not debounce only.Specialty
R
1

you can do it like that

<script lang='ts'>   
 import { debounce } from 'decko'

@debounce(1000)
 async doSearch() {
....
}
</script>
Rockery answered 4/8, 2020 at 12:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.