Repository: pmndrs/valtio (ts)

Issue:
Computed set doesn't work for objects and arrays
Reported by @italodeandra

Repro codesandbox: https://codesandbox.io/s/valtio-proxywithcomputed-update-array-bug-yb7mj?file=/src/App.tsx

Pull request description:
support object setters
close #97

Patch:
diff --git a/src/vanilla.ts b/src/vanilla.ts
index b88fa3b..64f72dd 100644
--- a/src/vanilla.ts
+++ b/src/vanilla.ts
@@ -129,7 +129,11 @@ export const proxy = <T extends object>(initialObject: T = {} as T): T => {
       if (childListeners) {
         childListeners.delete(notifyUpdate)
       }
-      if (refSet.has(value) || !isSupportedObject(value)) {
+      if (
+        refSet.has(value) ||
+        !isSupportedObject(value) ||
+        Object.getOwnPropertyDescriptor(target, prop)?.set
+      ) {
         target[prop] = value
       } else if (value instanceof Promise) {
         target[prop] = value
@@ -160,7 +164,7 @@ export const proxy = <T extends object>(initialObject: T = {} as T): T => {
       initialObject,
       key
     ) as PropertyDescriptor
-    if (desc.get) {
+    if (desc.get || desc.set) {
       Object.defineProperty(baseObject, key, desc)
     } else {
       proxyObject[key] = (initialObject as any)[key]


Test patch:
diff --git a/tests/computed.test.tsx b/tests/computed.test.tsx
index acf5b5c..ed4bfd7 100644
--- a/tests/computed.test.tsx
+++ b/tests/computed.test.tsx
@@ -110,3 +110,43 @@ it('computed getters and setters', async () => {
   expect(snapshot(state)).toMatchObject({ text: 'a', count: 0.5, doubled: 1 })
   expect(computeDouble).toBeCalledTimes(3)
 })
+
+it('computed setters with object and array', async () => {
+  const state = proxyWithComputed(
+    {
+      obj: { a: 1 },
+      arr: [2],
+    },
+    {
+      object: {
+        get: (snap) => snap.obj,
+        set: (state, newValue: any) => {
+          state.obj = newValue
+        },
+      },
+      array: {
+        get: (snap) => snap.arr,
+        set: (state, newValue: any) => {
+          state.arr = newValue
+        },
+      },
+    }
+  )
+
+  expect(snapshot(state)).toMatchObject({
+    obj: { a: 1 },
+    arr: [2],
+    object: { a: 1 },
+    array: [2],
+  })
+
+  state.object = { a: 2 }
+  state.array = [3]
+  await Promise.resolve()
+  expect(snapshot(state)).toMatchObject({
+    obj: { a: 2 },
+    arr: [3],
+    object: { a: 2 },
+    array: [3],
+  })
+})