I have a class using ES6 private fields and public getters that I need to be reactive using Vue 3's composition API. Currently, my set up looks something like this:
//store.ts
class Store {
#userName?: string
get userName() {
if(this.#userName !== undefined) return this.#userName
throw new Error('Cannot get userName before it is defined')
}
setUserName(newUserName: string) {
this.#userName = newUserName
}
}
const store = reactive(new Store())
export { store }
This store instance is then provided to components through the provide/inject API, and used like so
<template>
<span> {{ formattedUserName }} </span>
</template>
<script lang="ts">
import { defineComponent, toRefs, inject } from 'vue'
export default defineComponent({
setup(){
const store = inject('storeKey')
const formattedUserName = doSomeThings(store.userName)
return { formattedUserName }
})
But when I try to do this I get this error:
Cannot read private member #userName from an object whose class did not declare it
Could someone explain why this is, and if there's a way around it? I know TypeScript has the private keyword, but if possible I'd like to use private fields since they actually enforce the privacy at runtime.
Thanks!
Edit: After a bit more research, I've found the answer to the "why" part of my question. Vue 3 uses Proxy to track reactivity, which unfortunately does not work with private fields. If there is any way around this I would still love to know.
So short answer: There is no simple way to still use private fields and Proxy together. Private fields and Proxy, as they are implemented now (August 10th, 2021), are fundamentally incompatible. The second link in my original post has a ton of discussion on why that is and whether it should be changed, but without a new proposal it looks like this is just the way things are.
Here's how I got a store together that has
import {ref, reactive, computed} from 'vue'
class Store {
private internal_userName: Ref<string | undefined> = ref(undefined)
readonly userName = computed((): string => {
if(this.internal_userName.value !== undefined) return this.internal_userName.value
throw new Error('Cannot access userName before it is defined')
})
setUserName(newUserName: string) {
// do side effects here, such as setting localStorage keys
this.internal_userName.value = newUserName
}
}
const store = reactive(new Store())
//do things to export or provide your store here
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With