Dr.raider
Dr.raider

Reputation: 11

How to prevent component from destroying on route change in Vue?

I'm trying to show the list of products of different categories, which are retrieved from the server and cached, so when switching between categories, I don't have to make new requests every time. For that, I've used <keep-alive></keep-alive>, and It works partially.

<template>
  <div class="category">
    <h3 class="title animate-charcter"
        id="title">Choose the category</h3>
    <div class="category-container">
      <img class="category-item"
           data-cat="woman"
           @click="component = 'WomanClothes'"
           src="@/assets/hmwlogo.png">
      <img class="category-item"
           data-cat="electronics"
           @click="component = 'Electronics'"
           src="@/assets/electrologo2.jpg">
      <img class="category-item"
           data-cat="furniture"
           @click="component = 'Furniture'"
           src="@/assets/furniturelogo.png">
      <img class="category-item"
           data-cat="man"
           @click="component = 'ManClothes'"
           src="@/assets/hmmlogo.png">
    </div>

    <keep-alive>
      <component :is="component" />
    </keep-alive>
  </div>
</template>

<script>
  import WomanClothes from "./WomanClothes.vue"
  import Electronics from "./Electronics.vue"
  import Furniture from "./Furniture.vue"
  import ManClothes from "./ManClothes.vue"

  export default {
    name: "Category",
    components: { WomanClothes , Electronics , Furniture , ManClothes },
    data() {
      return {
        component: "WomanClothes"
      }
    },
  }
</script>

So when the user clicks on one of the category items, <component :is="component" /> dynamically updates itself. 4 other components that I've imported do API call, retrieves the data, and dynamically shows the data. The code is the same (I know it's efficient), just use different endpoints. So I will just show the code for one of them:

WomanClothes.vue:

<template>
  <div class="product-list-container">
    <input />
    <div class="row">
      <div class="list-item-container"
           v-for="product in products"
           :key="product.productId"
           @click="seeTheProduct(product)">
        <img :src="product.images[0].paths[0]"
             class="card-img-top"
             loading="lazy"
             alt="...">
        <div class="card-info">
          <h5 class="card-title">{{ product.name }}</h5>
          <h5 class="card-price">{{ product.price }} $.</h5>
        </div>
      </div>
    </div>
    <div class="load-more">
      <button v-if="loadMore"
              v-show="loadNow"
              @click="loadMoreProducts">
        Load more
      </button>
      <p v-else>End of the page</p>
    </div>
  </div>
</template>

<script>
  /* eslint-disable */
  import { db } from '../firebase';

  export default {
    name: "Products-list",
    props: {
      msg: String
    },

    data() {
      return {
        products: [],
        tempArr: [],
        path: '',
        latestDoc: null,
        loadMore: true,
        loadNow: true
      }
    },
    created() {
      console.log('from created')
      this.getProducts('products/woman-id/w-items')
    },
    destroyed() {
      console.log('on destroy')
    },
    methods: {
      loadMoreProducts() {
        this.loadNow = !this.loadNow;
        this.latestDoc = this.$store.getters.getDocHMW
        this.getProducts('products/woman-id/w-items')
      },
      async getProducts(category) {
        this.tempArr = []
        const ref = db.collection(category)
          .orderBy('price')
          .startAfter(this.latestDoc || 0)
          .limit(12)

        const data = await ref.get();

        data.docs.forEach(doc => {
          // this.tempArr.push(doc.data())
          this.products.push(doc.data())
        })

        this.latestDoc = data.docs[data.docs.length - 1];
        this.$store.commit('saveDoc', {doc: this.latestDoc, cat: 'hmw'})

        if (data.empty) {
          console.log('empty db')
          this.loadMore = false;
          this.$store.commit('saveBtnStatus', this.loadMore)
        }

        this.loadNow = true
      },
      // function to route to product info page
      seeTheProduct(product) {
        // saving to vuex and in localStorage
        this.$store.commit('checkProduct', product)
        // redirect the page
        this.$router.push({
          name: 'productinfo'
        })
      }
    },
  };
</script>

When seeTheProduct function gets called, the destroyed hook gets called, which means all cached data gets lost, when back to that route again. How can I make it, so when the route is being changed, the component which called for route change, doesn't get destroyed ?

Upvotes: 1

Views: 1629

Answers (1)

The Reddogg
The Reddogg

Reputation: 21

I believe you need to specify the 'key' prop on the router-view, like this:

<router-view :key="$route.fullPath"></router-view>

Then the keep-alive component will cache each component by key which evaluates to the url.

Upvotes: 2

Related Questions