Subscribe on changes!

Perserve immutable semantic on accessing reactive object properties

avatar
ycw
Dec 4th 2021

What problem does this feature solve?

The Problem:

readonly() immutable semantic silently lost on accessing :

// ex. 1
const x = ref(0)
const o = reactive({ 
  x: readonly(x), 
  //.. other mutable props ..  
})
++o.x // 1   ( =mutable  )

We may use no-setter computed() instead but intention is less obvious :

// ex. 2
const x = ref(0)
const o = reactive({ 
  x : computed( () => x.value )
  // .. other mutable props .. 
})
++o.x // warnings: Write operation failed: computed value is readonly

Feature request:

Vue should preserve immutable semantic when users assign a readonly() ref to a reactive property ( ex.1 ) ; and show warnings when immutable reactive properties are being mutated ( as if ex.2 ) .

End user experience:

When we receive a object wrapped in reactive() from "parent component" ( or "inject from provider" or "Pinia store instance" ) we're now confident to mutate any properties as data providers are now able-to and supposed-to handle access control for all data they provided in the single reactive/ref.

With Volar, we can even get early errors.

// ex. consume a Pinia store, mutate a readonly ref immutWallet
import useMyStore from './stores/myStore.js'
const store = useMyStore()    //-> a reactive
const onClick = () => ++store.immutWallet   // Volar: Error: key 'immutWallet' is readonly. 

This feature may immediately solve https://github.com/posva/pinia/issues/872#issuecomment-986019356

What does the proposed API look like?

Code sample

Data providers setup access control for properties

const t = ref(0)   // global time for ex.
const el = ref()   // in-tmpl ref for ex.
onMounted(() => 
  provide( 'o', reactive({  
    t: readonly(t), 
    el 
  }) )
)
setInterval(() => ++t.value, 1000)

Data consumers try to access an immutable property

const o = inject( 'o' )
const onClick = () => {
  o.el.dataset.triggerAt = o.t      // get... ok
  o.t = 0     // set... will warn in web console
}