vue.js

computed property is not updated by reactive dependency updating


I'm trying to create a class with a ComputedRef<> field, and assign its object to a component property. But the computed property is not updated.

/// Student.ts

import { computed, type ComputedRef } from "vue";

export class Student {
    age: number;
    adult: ComputedRef<boolean>;

    constructor(age: number) {
        this.age = age;

        const that = this;
        this.adult = computed(() => that.age >= 18);
    }
}
/// StudentComponent.vue

<script setup lang="ts">
import { Student } from './student';
import { ref } from 'vue';

const props = defineProps<{
    data: Student
}>();
const dataRef = ref(props.data);

</script>

<template>
    <div>age: {{ dataRef.age }}</div>
    <div>adult: {{ dataRef.adult }}</div>
    <button @click="++dataRef.age;">+1</button>
    <button @click="--dataRef.age;">-1</button>
</template>
</script>
/// App.vue

<script setup lang="ts">
import StudentComponent from './StudentComponent.vue';
import { Student } from './student';

const student = new Student(18);

</script>

<template>
    <StudentComponent :data="student"></StudentComponent>
</template>

The result is below:

"adult" value is not changed when "-1" button is clicked. How should I do it?


Solution

  • The computed value will only update when any referenced 'ref's are updated. Since the class property 'age' is just a number not a ref, the computed property will not know when a new value is set.

    It should work if you update your class to use a ref for the 'age' property:

    import { ref, type Ref, computed, type ComputedRef } from "vue";
    
    export class Student {
        age: Ref<number>;
        adult: ComputedRef<boolean>;
    
        constructor(age: number) {
            this.age = ref(age);
            this.adult = computed(() => this.age.value >= 18);
        }
    }
    

    make sure you update the age property through using studentInstance.age.value = newValue.

    You can also 'hide' the fact that you're using a ref in the class using getter/setter with a private property

    import {ref, type Ref, computed, type ComputedRef } from 'vue'
    
    
    export class Student {
      _age: Ref<number>
      adult: ComputedRef<boolean>
    
      get age() {
        return this._age.value
      }
    
      set age(newVal: number) {
            this._age.value = newVal
    }
    
      constructor(age: number) {
        this._age = ref(age)
        this.adult = computed(() => this._age.value >= 18)
      }
    }
    

    which will let you update values normally studentInstance.age = newValue