Repository: pmndrs/valtio
Pull request #202 (2021-07-31 21:06:56)

fix: return value in snapshot promise getter after promise is resolved
This fixes issue https://github.com/pmndrs/jotai/issues/624 with jotai/valtio where throwing a promise in a snapshot getter with the intention to suspend react render ends up causing an infinite loop by returning the value once the promise is resolved.

codesandbox[bot]: This pull request is automatically built and testable in [CodeSandbox](https://codesandbox.io).

  To see build info of the built libraries, click [here](https://ci.codesandbox.io/status/pmndrs/valtio/pr/202/builds/154830) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit eedc30a793d330854e953ab7882fb7093cb161fe:

|Sandbox| Source |
|--|--|
|[React](https://codesandbox.io/s/react-5cd02)| Configuration |
|[React Typescript](https://codesandbox.io/s/react-typescript-wdwpy)| Configuration |
|[React Browserify](https://codesandbox.io/s/react-browserify-1ednh)| Configuration |
|[React Snowpack](https://codesandbox.io/s/react-snowpack-9gm1w)| Configuration |
|[React Parcel](https://codesandbox.io/s/react-parcel-6b91j)| Configuration |

dai-shi: I fixed an issue around build tool. 93e71586a2689816e7e529cb4d0d2ce5237168bb
Please merge it and re-create .size-snapshot.json.
israelidanny: I merged master and added a test for this exact issue
dai-shi: Hi, I should have said generating size snapshot as follows:
```bash
rm .size-snapshot.json
yarn build -m 1
```
israelidanny: @dai-shi done

Patch:
diff --git a/.size-snapshot.json b/.size-snapshot.json
index 38bd34a..82a3cab 100644
--- a/.size-snapshot.json
+++ b/.size-snapshot.json
@@ -14,9 +14,9 @@
     }
   },
   "vanilla.js": {
-    "bundled": 6624,
-    "minified": 3127,
-    "gzipped": 1218,
+    "bundled": 6731,
+    "minified": 3175,
+    "gzipped": 1226,
     "treeshaked": {
       "rollup": {
         "code": 22,
diff --git a/src/vanilla.ts b/src/vanilla.ts
index 9570a81..283ca35 100644
--- a/src/vanilla.ts
+++ b/src/vanilla.ts
@@ -104,6 +104,9 @@ export const proxy = <T extends object>(initialObject: T = {} as T): T => {
           const errorOrPromise = (value as any)[PROMISE_ERROR] || value
           Object.defineProperty(snapshot, key, {
             get() {
+              if (PROMISE_RESULT in (value as any)) {
+                return (value as any)[PROMISE_RESULT]
+              }
               throw errorOrPromise
             },
           })


Test patch:
diff --git a/tests/snapshot.test.ts b/tests/snapshot.test.ts
new file mode 100644
index 0000000..6636726
--- /dev/null
+++ b/tests/snapshot.test.ts
@@ -0,0 +1,23 @@
+import { proxy, snapshot } from 'valtio'
+
+const sleep = (ms: number) =>
+  new Promise((resolve) => {
+    setTimeout(resolve, ms)
+  })
+
+it('getter returns value after promise is resolved', async () => {
+  const state = proxy<any>({ status: sleep(10).then(() => 'done') })
+  const snap = snapshot(state)
+
+  await new Promise((resolve) => {
+    resolve(snap.status)
+  })
+    .catch((thrown) => {
+      expect(thrown).toBeInstanceOf(Promise)
+      return thrown
+    })
+    .then((value) => {
+      expect(value).toBe('done')
+      expect(snap.status).toBe('done')
+    })
+})