Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue component prop change does not trigger rerender

In my Vue 2 application I have a big object that is passed from the root component down several levels to some child components as a prop. When I change some property of the object the child components should update and rerender. In some cases they do, in other cases they don't. I need some help spotting why it does not work.

Here is a child component, which does not update:

<template>
<div class="upgradeBar">
    {{level}}
    <div
            v-for="lvlNum in maxLevel + 1"
            class="level"
            v-bind:class="{reached: isLevelReached(lvlNum - 1)}"
    ></div>

    <button
            class="btnUpgrade"
            @click="onLevelUp()"
            v-if="!isLevelReached(maxLevel)"
    >
        +
    </button>
</div>
</template>


<script lang="ts">
import {Component, Prop, Vue} from 'vue-property-decorator';
import Upgradable from "../../../models/Upgradable";

@Component()
export default class UpgradeBar extends Vue {
    name: 'UpgradeBar';

    @Prop() entity: Upgradable;

    get level(): number {
        return this.entity.level;
    }

    get maxLevel(): number {
        return this.entity.MAX_LEVEL;
    }

    onLevelUp() {
        this.entity.levelUp();
    }

    isLevelReached(level: number): Boolean {
        return this.entity.level >= level;
    }
}
</script>

The component is called like this:

<UpgradeBar :entity="entity" />

All the code works. When I click the btnUpgrade button entity.level is indeed changed, but I need to close and reopen the component to see the changes. Also the browser dev tool does not show the change instantly. I need to click on the component to refresh the values in the debugger.

EDIT: The entity class looks basicly like this (excerpt):

class Entity {
    name: string = 'some name';
    level: number = 1;
}

I searched deeper and it seems to boils down to this: Some properties of the object are reactive (they have getters / setters created by vue) and some don't. entity.name has a setter, so changing it updates the component. entity.level does not. Here's the question: Why are they treated differently? Here is a log:

console.log of entity

like image 289
Reynicke Avatar asked May 03 '18 16:05

Reynicke


1 Answers

Can't tell for sure without seeing the code for entity.levelUp, but it seems like a reactivity issue, that may be solved by using Vue.$set inside that function.

You can confirm this being the case by adding this.$forceUpdate(); after this.entity.levelUp();

update

this._level = this._level + 1;

can be changed to

Vue.$set(this, _level, this._level + 1); You will need to import Vue in that component/file to access the $set function

like image 190
Daniel Avatar answered Sep 23 '22 19:09

Daniel