Skip to content

Commit

Permalink
Fix computed values breaking when the store is passed to Object.freeze
Browse files Browse the repository at this point in the history
  • Loading branch information
Kate Corcoran committed Dec 6, 2024
1 parent bb3beae commit cc591a4
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 10 deletions.
29 changes: 19 additions & 10 deletions src/impl/computed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,25 @@ export function addComputed(instance: object, descriptors: [string, PropertyDesc
descriptors.forEach(([key, desc]) => {
const {get, set} = desc
if (get) {
let ref = set
? computed({get: get.bind(instance), set: set.bind(instance)})
: computed(get.bind(instance))

Object.defineProperty(instance, key, {
value: ref, // vue unwraps this Ref automatically when accessing it
writable: desc.writable,
enumerable: desc.enumerable,
configurable: true
})
// We *could* just set `value: ref` in the descriptor and Vue will automatically unwrap the ref, however that
// causes errors if the object is passed through `Object.freeze()`, since the underlying value is a ref, but the
// proxy returns a different value: https://github.com/vuejs/core/issues/3024
if (set) {
const ref = computed({get: get.bind(instance), set: set.bind(instance)})
Object.defineProperty(instance, key, {
get: () => ref.value,
set: (v) => ref.value = v,
enumerable: desc.enumerable,
configurable: true
})
} else {
const ref = computed(get.bind(instance))
Object.defineProperty(instance, key, {
get: () => ref.value,
enumerable: desc.enumerable,
configurable: true
})
}
}
})
}
15 changes: 15 additions & 0 deletions tests/vuestore_create.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,21 @@ describe("createStore", () => {
expect(spies.late).to.be.called.with(400, 40)
});

it("computed properties should work after Object.freeze", async () => {
class Store {
plain = 10

get computed() {
return this.plain + 1
}
}

let store = createStore(new Store())
expect(store.computed).to.equal(11)
Object.freeze(store)
expect(() => store.computed).not.to.throw()
});

testWatches(c => c, Object, createStore)

it("watches should be created for indirect (string) references", async () => {
Expand Down
16 changes: 16 additions & 0 deletions tests/vuestore_extends.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,22 @@ describe("@VueStore + extends VueStore", () => {
expect(spies.late).to.be.called.with(400, 40)
});

it("computed properties should work after Object.freeze", async () => {
@VueStore
class Store extends VueStore {
plain = 10

get computed() {
return this.plain + 1
}
}

let store = new Store()
expect(store.computed).to.equal(11)
Object.freeze(store)
expect(() => store.computed).not.to.throw()
});

testWatches(VueStore, VueStore, v => v)

it("methods should be accessible and reactive", async () => {
Expand Down

0 comments on commit cc591a4

Please sign in to comment.