zundo: When using the handleSet method to cooldown State pushes, diff gets the wrong pastState.

The pastState should be the last state actually pushed to the history (in other words, the last state handleSet was called on).

diff: (pastState, currentState) => {
  const delta = diff(currentState, pastState);
  console.log('delta', delta)
  
  const newStateFromDiff = delta.reduce(
    (acc, difference) => {
      type Key = keyof typeof currentState
      if (difference.type === 'CHANGE') {
        const pathAsString = difference.path.join('.') as Key
        acc[pathAsString] = difference.value
      }
      return acc
    },
    {} as Partial<typeof currentState>,
  );
  return Object.keys(newStateFromDiff).length > 0 ? newStateFromDiff : null;
},
handleSet: (handleSet) => (state) => {
  if (!state) return

  debouncedHandleSetTimeoutID && clearTimeout(debouncedHandleSetTimeoutID)
  debouncedHandleSetTimeoutID = window.setTimeout(() => {
    handleSet(state)
  }, 300)
}

let debouncedHandleSetTimeoutID: number | null = null

About this issue

  • Original URL
  • State: closed
  • Created 8 months ago
  • Comments: 16 (7 by maintainers)

Most upvoted comments

Thanks for making zundo!

I need the denounce to group together changes that happen very quickly after one another. In my application I have a graph, and when I move a node in that graph, every pixel counts as a new state. Of course when I press undo, I just want it to go back to its last position where I started the drag from.

As general solution i thought of denouncing the handleset method and only saving the state after nothing changed for a few hundred milliseconds. But right now it just saves the last state that got pushed (so a movement from y=10 to y=100 would save y=99)

Let me know if I can help in any way with this issue. And thank you for taking the time.

Here is a Sandbox example: https://codesandbox.io/s/zundo-forked-233fhc?file=/src/App.tsx

The handleSet is debounced by 1 second. So if you wait more than 1 second between clicking increment, the history is 0, 1, 2, 3 like you expect.

Now, if I click increment rapidly 5 times, the history should be [0] (with current state being 5). What actually happens is, that the history is [4]

This happens because, even though the handleSet is being called with the previous state every time the state changes. So for increase from 0 to 1, handleSet saves 0 to the history. When you click 5 times rapidly, the first 4 increases are dropped, and only when increasing from 4 to 5, handleSet is called with 4, and saved to the history.

In other words:

When not dropping handleSet calls, the delta is between the current state, and the last state State: 0 -> State: 1 State: 1 -> State: 2 State: 2 -> State: 3 etc

When dropping handleSets, the delta should be between the last saved state, and the current one. So in this case: State: 0 -> State: 5