Egor Konovalov
Egor Konovalov

Reputation: 77

How to make a class getter reactive in Vue.js 3 with Composition API?

I'm trying to make a class instance property reactive to display the error message if authentication fails.

userModel.ts

export class User {
  private error: string;

  set errorMessage(errorMessage: string) {
    this.error = errorMessage;
  }
  get errorMessage() {
    return this.error
  }

// other stuff...
}

LoginView.vue

import { User } from "./models/userModel";
import { ref } from 'vue';

const user = new User();
const errorMessage = ref(user.errorMessage); // <--- This is not working.
const errorMessage = ref(user.error); // <--- Neither this even if user.error property is public.

No warnings or exceptions, reactive value just remains empty. What I am missing?

Upvotes: 5

Views: 4461

Answers (3)

Gabriel soft
Gabriel soft

Reputation: 563

I did do something quite similar, but it didn't work

@vueuse provides a createsharedcomposable that allows reactivity across the entire application, using class structure won't be beneficial

Upvotes: 0

Kapcash
Kapcash

Reputation: 6909

Here, you are creating new reactive variables, with the initial value being the one in your class. Your class and your ref are unrelated.

If you want your class to have reactive properties, you have to instanciate them this way:

export class User {
  private error: Ref<string> = ref('');

  public errorMessage: WritableComputedRef<string> = computed({
    get() {
      return this.error.value
    },
    set(errorMessage: string) {
      this.error.value = errorMessage;
    },
  })
}

Side note: I'd recommend to use the "composables" approach instead of using classes, because class instances are not compatible with SSR.

export function useUser () {
  const user: User = reactive({})
  // other stuff...

  const error: Ref<string> = ref('');

  const errorMessage: WritableComputedRef<string> = computed({
    get() {
      return this.error.value
    },
    set(errorMessage: string) {
      this.error.value = errorMessage;
    },
  })

  return { user, errorMessage }
}

Upvotes: 7

Estus Flask
Estus Flask

Reputation: 222464

Accessing a primitive property by value like ref(user.error) can't be reactive, no matter it's a getter or not, in this case it doesn't differ from ref(undefined).

For own enumerable property it could be:

let user = reactive(new User());
let { error } = toRefs(user);

It won't handle property accessors, they need to be explicitly accessed with computed property:

let errorMessage = computed({
  get: () => user.errorMessage,
  set: value => { user.errorMessage = value }
});

Whether it's a getter or regular property doesn't affect how a computed works because an accessor is defined on class prototype and accesses reactive this.error (this can be a problem when non-reactive this is bound in a constructor, like is shown here and here).

Upvotes: 5

Related Questions