Search code examples
javascriptvue.jslodashcomputed-propertiesdebouncing

VueJS Computed Property with Lodash Debounce


I am filtering through an array of large items based on the user input and that becomes slow. So, wanting to implement the Lodash debounce. However, the debounce isn't affecting anything.

The following is the search input field in HTML where the user types to filter through the array.

<input v-model="search" type="text"/>

In the following, I am looping through the filteredIcons computed property to show the items.

<section v-if="icons.length">
  <button v-for="(icon, index) in filteredIcons" :key="index">
    <span v-html="icon.icon"></span>
    <span v-text="icon.name"></span>
  </button>
</section>

And this is where the icons large array and filtering through the computed property.

import { debounce } from "lodash";

export default {
  data() {
    return {
      icons: [
               {
                 "author":"svg",
                 "icon":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M13 16v5a1 1 0 01-1 1H9l-3-6a2 2 0 01-2-2 2 2 0 01-2-2v-2c0-1.1.9-2 2-2 0-1.1.9-2 2-2h7.59l4-4H20a2 2 0 012 2v14a2 2 0 01-2 2h-2.41l-4-4H13zm0-2h1.41l4 4H20V4h-1.59l-4 4H13v6zm-2 0V8H6v2H4v2h2v2h5zm0 2H8.24l2 4H11v-4z\"/></svg>",
                 "name":"icon-announcement.svg"
               },
               {
                 "author":"svg",
                 "icon":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M20 9v10a2 2 0 01-2 2H6a2 2 0 01-2-2V9a2 2 0 01-2-2V5c0-1.1.9-2 2-2h16a2 2 0 012 2v2a2 2 0 01-2 2zm0-2V5H4v2h16zM6 9v10h12V9H6zm4 2h4a1 1 0 010 2h-4a1 1 0 010-2z\"/></svg>",
                 "name":"icon-archive.svg"
               }
             ],
      search: ""
    };
  },
  computed: {
    filteredIcons: {
      get() {
        return this.icons;
      },
      set: debounce(function() {
        this.icons = this.icons.filter(icon => {
          icon.name.toLowerCase().includes(this.search.toLowerCase());
        });
      }, 500)
    }
  }
}

I am struggling to understand why this isn't working and what would be the best way to go about filtering through a large data items in an array by implementing the debounce. Any hints would be appreciated.


Solution

  • Your filter function is not returning the result of String#includes, which results in all items not matching the filter, leading to an empty array. You could either add a return keyword to return the result:

    const filteredIcons = this.icons.filter(icon => {
      return icon.name.toLowerCase().includes(this.search.toLowerCase())
    })
    

    ...or you could remove the brackets and semicolon:

    const filteredIcons = this.icons.filter(icon => icon.name.toLowerCase().includes(this.search.toLowerCase()))
    

    Also, the debounce should be occurring on the user input; not on the computed setter. I would use a watcher on the input's v-model (i.e., search), and then debounce the result:

    export default {
      watch: {
        search: {
          handler(search) {
            this.setIconsDebounced(search)
          },
          immediate: true
        }
      },
      methods: {
        setIconsDebounced: _.debounce(function(search) {
          this.filteredIcons = this.icons.filter(/*...*/)
        }, 500)
      }
    }
    

    That eliminates the need for the computed prop, but you still need a variable to hold the filtered icons, so declare one in your data props:

    export default {
      data() {
        return {
          filteredIcons: []
        }
      }
    }
    

    demo