Search code examples
javascriptvue.jsvuex

Refactoring getters Vue.js (Vuex)


I am trying to refactor two getters in vuex. In my state I have some hard coded array of objects like this:

 state: {
    color: 'All colors',
    size: 'All sizes',

    allShoes: [
      {
        productId: 1,
        productno: 1234,
        type: 'sneakers',
        brand: 'Nike',
        model: 'Air Zoom',
        gender: 'Men',
        size: ['39', '40', '41', '42', '43', '44', '45'],
        price: 120,
        color: 'red',
        outerMaterial: 'Textile',
        lining: 'Textile',
        sole: 'Textile',
        casingThickness: 'Thin lining',
        fabric: 'Knitwear',
        colors: ['red', 'blue', 'green'],
        image: require('@/assets/images/nike-air-zoom.jpg'),
      },
      {
        productId: 2,
        productno: 1235,
        type: 'sneakers',
        brand: 'Adidas',
        model: 'Ultra Boost',
        gender: 'Men',
        size: ['41', '42', '43', '44', '45'],
        price: 130,
        color: 'white',
        outerMaterial: 'Textile',
        lining: 'Textile',
        sole: 'Textile',
        casingThickness: 'Thin lining',
        fabric: 'Knitwear',
        colors: ['red', 'blue', 'orange'],
        image: require('@/assets/images/adidas-ultra.jpg'),
      },

You can see the full application here

As you can see I want to filter my products by color and size. That works just fine with the following getters:

  getters: {
    getProductById: (state) => (id) => {
      return state.allShoes.find((shoe) => shoe.productId === id);
    },
    getProductsByGender: (state) => (gender) => {
      if (state.color === 'All colors' && state.size === 'All sizes') {
        return state.allShoes.filter((shoe) => shoe.gender === gender);
      } else {
        return state.allShoes.filter((shoe) => {
          return (
            (state.color === 'All colors' ||
              (shoe.color === state.color && shoe.gender === gender)) &&
            (state.size === 'All sizes' || shoe.size.includes(state.size)) &&
            shoe.gender === gender
          );
        });
      }
    },
    getProductsByType: (state) => (type) => {
      if (state.color === 'All colors' && state.size === 'All sizes') {
        return state.allShoes.filter((shoe) => shoe.type === type);
      } else {
        return state.allShoes.filter((shoe) => {
          return (
            (state.color === 'All colors' ||
              (shoe.color === state.color && shoe.type === type)) &&
            (state.size === 'All sizes' || shoe.size.includes(state.size)) &&
            shoe.type === type
          );
        });
      }
    },
  }

The problem I am having is that this is kind of a duplicate code and I would like to refactor the getProductsByGender and getProductsByType getters into one. In the getProductsByGender I am using the filter array method but I need to access the shoe.gender in my object. In the getProductsByType I need to access the shoe.type. I can't figure out a way to refactor these two getters or maybe it's ok to use them like this? It feels like im not following the DRY principle. Is there a way to use a mixin maybe? I would appreciate if someone could point me in the right direction.


Solution

  • I don't think your function itself is bad. You can indeed boil it down to just one though. Keep in mind that if your page should be getting several thousand items you should consider doing this on the backend with some form of search-engine (elastic, algolia).

    products(options) {
      if (state.color === "All colors" && state.size == "All sizes") {
        return state.allShoes.filter((shoe) => shoe[options.category] === options.filter);
      } else {
        return state.allShoes.filter((shoe) => {
          return (
            (state.color === "All colors" ||
              (shoe.color === state.color && shoe[options.category] === options.filter)) &&
            (state.size === "All sizes" || shoe.size.includes(state.size)) &&
            shoe[options.category] === options.filter
          );
        });
      }
    }
    
    // Example to call the function
    const products = this.products({
      category: "gender",
      filter: "Female",
    });