Skip to content

Vue 3: Why get same value before update object

I have 2 components in my project where I tried create query based search filter

  1. PostsList component:
<template>
  <div>
    <PostFilter @change-filter="setFilters" />
    <h3>Active filters</h3>
    <pre>{{ activeFilters }}</pre>
    <h3>Updated filters</h3>
    <pre>{{ updatedFilters }}</pre>
  </div>
</template>
<script>
import PostFilter from "../../components/posts/PostFilter.vue";
export default {
  components: {
    PostFilter,
  },
  data() {
    return {
      updatedFilters: {},
      activeFilters: {
        category: "",
        query: "",
        perPage: 5,
        page: 1,
      },
    };
  },

  methods: {
    setFilters(updatedFilters) {
      console.log("Active filters");
      console.log(this.activeFilters);

      console.log("Updated filters");
      console.log(updatedFilters);

      this.$router.push({ query: updatedFilters });
      if (this.$route.query.page) {
        updatedFilters.page = 1;
      }
      this.activeFilters = updatedFilters;
    },
    objectEquals(obj1, obj2) {
      for (var i in obj1) {
        // eslint-disable-next-line
        if (obj1.hasOwnProperty(i)) {
          // eslint-disable-next-line
          if (!obj2.hasOwnProperty(i)) return false;
          if (obj1[i] !== obj2[i]) return false;
        }
      }
      for (var j in obj2) {
        // eslint-disable-next-line
        if (obj2.hasOwnProperty(j)) {
          // eslint-disable-next-line
          if (!obj1.hasOwnProperty(j)) return false;
          if (obj1[j] !== obj2[j]) return false;
        }
      }
      return true;
    },
  },
};
</script>
  1. PostFilter component:
<template>
  <div>
    <select class="category" v-model="filters.category" name="category">
      <option value="" selected>All categories</option>
      <option
        v-for="(category, index) in categories"
        :key="index"
        :value="category.id"
      >
        {{ category.title }}
      </option>
    </select>
    <input
      class="search"
      v-model="filters.query"
      type="search"
      name="query"
      placeholder="Search..."
    />
    <select class="perPage" v-model="filters.perPage" name="perPage">
      <option value="5">5 items / page</option>
      <option value="10">10 items / page</option>
      <option value="15">15 items / page</option>
      <option value="20">20 items / page</option>
    </select>
  </div>
</template>

<script>
export default {
  emits: ["change-filter"],

  created() {
    const query = this.$route.query;
    if (
      query &&
      Object.keys(query).length > 0 &&
      query.constructor === Object &&
      this.hasFilterKeys(query)
    ) {
      this.filters = { ...this.filters, ...this.$route.query };
    }
    this.fecthCategories();
  },
  data() {
    return {
      filters: {
        category: "",
        query: "",
        perPage: 5,
      },
      awaitingSearch: false,
      categories: [],
    };
  },
  watch: {
    "filters.category": function (currentValue, oldValue) {
      if (oldValue !== currentValue) {
        this.emitFilters();
      }
    },
    "filters.query": function (currentValue, oldValue) {
      // eslint-disable-next-line
      if (oldValue != currentValue) {
        if (!this.awaitingSearch) {
          setTimeout(() => {
            this.emitFilters();
            this.awaitingSearch = false;
          }, 1000);
        }
        this.awaitingSearch = true;
      }
    },
    "filters.perPage": function (currentValue, oldValue) {
      if (oldValue !== currentValue) {
        this.emitFilters();
      }
    },
  },
  methods: {
    emitFilters() {
      let cleanFilters = this.cleanObject(this.filters);
      this.$emit("change-filter", cleanFilters);
    },
    cleanObject(obj) {
      for (var propName in obj) {
        if (
          obj[propName] === null ||
          obj[propName] === undefined ||
          obj[propName] === ""
        ) {
          delete obj[propName];
        }
      }
      return obj;
    },
    async fecthCategories() {
      this.categories = [
        {
          id: 1,
          title: "Anatomy",
        },
        {
          id: 2,
          title: "Bialogy",
        },
        {
          id: 3,
          title: "Algebra",
        },
        {
          id: 4,
          title: "Chemistry",
        },
        {
          id: 5,
          title: "Computer science",
        },
        {
          id: 6,
          title: "English",
        },
      ];
    },
    hasFilterKeys(obj) {
      for (const key in this.filters) {
        // eslint-disable-next-line
        if (obj.hasOwnProperty(key) && obj[key] != "") {
          return true;
        }
      }
      return false;
    },
  },
};
</script>

I must send one API request when filters is change/update. But why I get same values in activeFilters and in coming filters from method setFilters() of PostsList component on everytime?

Why I lose old value of activeFilters before set new values in my case?

You can see here real working example

Also here example video

<blockquote class="imgur-embed-pub" lang="en" data-id="a/Y4gauRM"  ><a href="//imgur.com/a/Y4gauRM">Same object values on search</a></blockquote><script async src="//s.imgur.com/min/embed.js" charset="utf-8"></script>

Answer

The object references in chrome developer console is “alive”.

Consider this example:

let x = {a: 'b'}
console.log("Active filters", x);
x.a = 'c';

this will output:

Active filters {a: "c"}

instead of Active filters {a: "b"}


You can use the spread operator to create a new object as value of activeFilters in your example to see different values:

this.activeFilters = {...updatedFilters};