Subscribe on changes!

Dynamically set reactive nested property is different when set during initialization

avatar
Nov 19th 2021

Version

3.2.22

Reproduction link

sfc.vuejs.org/

Steps to reproduce

  • Run repro

What is expected?

Both isReactive() should yield the same result

What is actually happening?

Setting a nested reactive() object dynamically is different from initialiazing it: reactive({ nested })


I found this in Pinia, where the following code doesn't work:

import { ref, reactive } from 'vue'
import { storeToRefs } from 'pinia'

const useTest = defineStore('test', () => {
    const foo = ref('foo')
    const bar = reactive({
        baz: 'something'
    }

    return {
        foo, bar
    }
}

const store = useTest()

const { foo, bar } = storeToRefs(store)
// bar.value is undefined

This is because pinia stores are reactive() objects that have their properties set dynamically (for various reasons like cross store usage and HMR).

storeToRefs() code is

export function storeToRefs(store) {
  store = toRaw(store) // avoid triggering getters

  const refs = {}
  for (const key in store) {
    const value = store[key]
    if (isRef(value) || isReactive(value)) {
      refs[key] = toRef(store, key)
    }
  }

  return refs
}
avatar
Nov 19th 2021

I'd say that this is expected. the raw source object for the first store does contain a proxy as its nested property because you defined it that way. Would you expect toRaw() to somehow unwrap that? that would alter the source object.

avatar
Nov 19th 2021

I would expect to both reactive({ nested }) and reactive({}).nested = nested to work the same way with nested = reactive({}) 🤔

I can do toRaw(store).nested = nested though but it won't be observed (which is intentional with toRaw()) so that would be problematic

But I see what you mean: reactive({}).nested = ... isn't affecting the original object... Maybe it's reasonable needing to change it this way

avatar
Nov 19th 2021

I think you are right, it makes sense for toRaw() to not have nested