Klick
Klick

Reputation: 1653

Vue2 - how to recreate component?

How can I recreate a whole component after button click.

Let say I'm in Component "UserPanel" and there is a button call "Refresh". When I click that button I would like to destroy component and create it from the scratch. I don't wont to use option like "vm.$forceUpdate()" because it doesn't help in my case.

Is it any way to do it?

My app code:

App.vue:

<template>
    <div id="main-cont">
        <NavBar></NavBar>
        <router-view></router-view>
    </div>
</template>

<script>

import NavBar from './components/TopBar/NavBar';
import {mapActions,mapGetters} from 'vuex';
import axios from 'axios';

    export default {
        name: 'App',
        components: {
            NavBar,
        },
        computed:{
            ...mapGetters(['isLoggedIn'])
        },
        methods:{
            ...mapActions(['loadLanguage','setToken','setUserLogged','loadUserProfile'])
        },
        created(){
            this.loadLanguage();
            this.setToken();
            let userLoggedIn = document.head.querySelector('meta[name="logged"]').content;
            if(userLoggedIn){
                this.setUserLogged();
                this.loadUserProfile();
            }

        }

    }

</script>

<style scoped>
    #main-cont{
        height: 100%;
    }
</style>

main.js:

import Vue from 'vue';
import VueRouter from 'vue-router';
import VueCookie from 'vue-cookie';
import store from './store';
import App from './App';


//Components
import Main from './components/main/Main';
import UserRegister from './components/user/UserRegister';
import ResetPassword from './components/user/ResetPassword';
import UserEdit from './components/user/UserEdit';
import UserView from './components/user/UserView.vue';
import GameMain from './components/game/GameMain';
import GamesList from './components/main/GameList';
import Hall from './components/main/Hall';
import Language from './components/main/Language';
import GameCreate from './components/game/GameCreate';

//Plugins
import langPlugin from './langPlugin';
import VTooltip from 'v-tooltip';

Vue.use(VueRouter);
Vue.use(VueCookie);
Vue.use(langPlugin);

export const router = new VueRouter({
    mode: 'history',
    routes: [
        {path: '/', component: Main},
        {path: '/user-register', component: UserRegister},
        {path: '/user-edit', component: UserEdit},
        {path: '/password-reset', component: ResetPassword},
        {path: '/user', component: UserView},
        {path: '/game', component: GameMain},
        {path: '/game-create', component: GameCreate},
        {path: '/games-list', component: GamesList},
        {path: '/hall-of-fame', component: Hall},
        {path: '/language', component: Language},

    ]
});

new Vue({
    router,
    store,
    render: h => h(App)
}).$mount('#app');

Component to Reload. GameCreate:

<Template>
    <div>
        <button @click="reloadThisComponent"></button>
    </div>
</Template>
<script>
    export default{
        name: 'GameCreate',
        methods:{
            reloadThisComponent(){

            }
        }
    }
</script>

Thank you.

Upvotes: 2

Views: 5375

Answers (2)

Ekushisu
Ekushisu

Reputation: 461

EDIT (with the new question details) : Since you're using view router and your component is registred as a route, juste simply add the following to your rebuild method in your Game component this should works fine

this.$router.go(this.$router.currentRoute)

const router = new VueRouter({
  mode: 'history',

})

new Vue({
    router,
  el: '#app',
  methods: {
    reload: function() {    
       this.$router.go(this.$router.currentRoute)
    }
  },
  created() {
    console.log("Hey")
  }
})
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<script src="https://npmcdn.com/vue-router/dist/vue-router.js"></script>

<div id="app">
    <button @click="reload">Reload</button>
  <router-view></router-view>
</div>


The simple way to get to the goal is to set a boolean in v-if on your component. Then switch true/false the boolean. When v-if is false the component is destroyed and will be reinstanciate after.


To do this, there is two way. What we want is to change de state of the parent component that will say if we print our component or not. The first way to do it is by using a State Management plugin like VueX, but it's a little bit too much for what we simply want to do. To be simpliest, we have to trigger an event from your component, that will trigger the state change on the parent.

In the exemple bellow, when you click on the reset button inside MyComponent,custom event named "reset" is emitted. In the parent component, we have a showMyComponent boolean on our MyComponent tag and a listener @reset that will trigger the method named "resetMyComponent" when the event "reset" is emmited by our MyComponent.

Here is a few resources :

Hope it's more clear now

var MyComponent = Vue.component('my-component', {
    name : "my-component",
    template : "#my-component-template",
    data(){
        return {
            interval : null,
            count : 0
        }
    },
    created() {
        console.log("MyComponent is created")       
        this.interval = setInterval(() => {
            this.count++
        },1000)     
    },
    destroyed() {
        console.log("MyComponent is destroyed")
        clearInterval(this.interval)
    }
});


new Vue({
    el: "#app",
    components : {
        MyComponent
    },
    data: {
        showMyComponent : true
    },
    methods : {
        resetMyComponent() {
            this.showMyComponent = false;
            Vue.nextTick(() => {
                this.showMyComponent = true;
            });
        }
    }
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
    <my-component v-if="showMyComponent" @reset="resetMyComponent"></my-component>
</div>


<script type="text/x-template" id="my-component-template">
    <div style="padding: 15px">
        <p>My component is created since {{count}} seconds</p>      
        <button @click="$emit('reset')">Reset my component</button>
    </div>
</script>

Upvotes: 5

Shahzod O&#39;ralov
Shahzod O&#39;ralov

Reputation: 112

There are multiple ways of recreating components. The most efficient way is to change the component key. What we do here is we will supply a key attribute so Vue knows that a specific component is coupled or tied to a specific piece of data. If the key stays the same, it won't change the component, but if the key changes, Vue knows that it should get rid of the old component and re-create a new one.

Here is a very basic way of doing it:

<template>
<component-to-re-render :key="componentKey" />
</template>
export default {
  data() {
    return {
      componentKey: 0,
    };
  },
  methods: {
    forceRerender() {
      this.componentKey += 1;
    }
  }
}

Every time that forceRerender is called, our prop componentKey will change. When this happens, Vue will know that it has to destroy the component and create a new one. What you get is a child component that will re-initialize itself and “reset” its state. this simple and elegant way is solving the most common challenge we face in the Vue app development!

You can also check other possible ways od doing this: https://medium.com/emblatech/ways-to-force-vue-to-re-render-a-component-df866fbacf47

Upvotes: 2

Related Questions