Repository: pmndrs/valtio (ts)

Issue:
Bug with history?
Bug with history or I just don't understand how it should work.

[sandbox link](https://codesandbox.io/s/react-typescript-forked-m0s2f?file=/src/App.tsx)

After a few clicks of undo, it just stops working.

Expected: 
initial value 0
add -> undo -> add -> add -> add -> undo -> undo -> undo -> add -> add -> undo -> undo -> 0


Actual: 
initial value 0
add -> undo -> add -> add -> add -> undo -> undo -> undo -> add -> add -> undo -> undo -> 3

Pull request:
fix(utils): proxyWithHistory to undo to initialValue
close #194 

Patch:
diff --git a/.size-snapshot.json b/.size-snapshot.json
index b02d4e6..92fd3cc 100644
--- a/.size-snapshot.json
+++ b/.size-snapshot.json
@@ -38,14 +38,14 @@
     }
   },
   "utils.js": {
-    "bundled": 8190,
-    "minified": 3870,
-    "gzipped": 1486
+    "bundled": 8427,
+    "minified": 4012,
+    "gzipped": 1497
   },
   "utils.mjs": {
-    "bundled": 6916,
-    "minified": 3262,
-    "gzipped": 1411,
+    "bundled": 7137,
+    "minified": 3388,
+    "gzipped": 1425,
     "treeshaked": {
       "rollup": {
         "code": 45,
diff --git a/src/utils.ts b/src/utils.ts
index 92851d9..7225a0b 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -394,6 +394,10 @@ export const proxyWithHistory = <V>(initialValue: V) => {
       if (proxyObject.canUndo()) {
         proxyObject.value = proxyObject.history.wip =
           proxyObject.history.snapshots[--proxyObject.history.index]
+        // refresh snapshot to use again
+        proxyObject.history.snapshots[proxyObject.history.index] = snapshot(
+          proxyObject
+        ).value as V
       }
     },
     canRedo: () =>
@@ -402,6 +406,10 @@ export const proxyWithHistory = <V>(initialValue: V) => {
       if (proxyObject.canRedo()) {
         proxyObject.value = proxyObject.history.wip =
           proxyObject.history.snapshots[++proxyObject.history.index]
+        // refresh snapshot to use again
+        proxyObject.history.snapshots[proxyObject.history.index] = snapshot(
+          proxyObject
+        ).value as V
       }
     },
     saveHistory: () => {
@@ -410,6 +418,7 @@ export const proxyWithHistory = <V>(initialValue: V) => {
       ++proxyObject.history.index
     },
   })
+  proxyObject.saveHistory()
   subscribe(proxyObject, (ops) => {
     if (
       ops.some(


Test patch:
diff --git a/tests/history.test.tsx b/tests/history.test.tsx
index 5db95ea..b7577c8 100644
--- a/tests/history.test.tsx
+++ b/tests/history.test.tsx
@@ -46,6 +46,15 @@ it('simple count', async () => {
 
   fireEvent.click(getByText('undo'))
   await findByText('count: 1')
+
+  fireEvent.click(getByText('undo'))
+  await findByText('count: 0')
+
+  fireEvent.click(getByText('inc'))
+  await findByText('count: 1')
+
+  fireEvent.click(getByText('undo'))
+  await findByText('count: 0')
 })
 
 it('count in object', async () => {
@@ -91,4 +100,67 @@ it('count in object', async () => {
 
   fireEvent.click(getByText('undo'))
   await findByText('count: 1')
+
+  fireEvent.click(getByText('undo'))
+  await findByText('count: 0')
+
+  fireEvent.click(getByText('inc'))
+  await findByText('count: 1')
+
+  fireEvent.click(getByText('undo'))
+  await findByText('count: 0')
+})
+
+it('count in nested object', async () => {
+  const state = proxyWithHistory({ nested: { count: 0 } })
+
+  const Counter: React.FC = () => {
+    const snap = useSnapshot(state)
+    return (
+      <>
+        <div>count: {snap.value.nested.count}</div>
+        <button onClick={() => ++state.value.nested.count}>inc</button>
+        <button onClick={snap.undo}>undo</button>
+        <button onClick={snap.redo}>redo</button>
+      </>
+    )
+  }
+
+  const { getByText, findByText } = render(
+    <StrictMode>
+      <Counter />
+    </StrictMode>
+  )
+
+  await findByText('count: 0')
+
+  fireEvent.click(getByText('inc'))
+  await findByText('count: 1')
+
+  fireEvent.click(getByText('inc'))
+  await findByText('count: 2')
+
+  fireEvent.click(getByText('inc'))
+  await findByText('count: 3')
+
+  fireEvent.click(getByText('undo'))
+  await findByText('count: 2')
+
+  fireEvent.click(getByText('redo'))
+  await findByText('count: 3')
+
+  fireEvent.click(getByText('undo'))
+  await findByText('count: 2')
+
+  fireEvent.click(getByText('undo'))
+  await findByText('count: 1')
+
+  fireEvent.click(getByText('undo'))
+  await findByText('count: 0')
+
+  fireEvent.click(getByText('inc'))
+  await findByText('count: 1')
+
+  fireEvent.click(getByText('undo'))
+  await findByText('count: 0')
 })