Skip to content

Comments

fix equality fn type and update docs#144

Merged
charkour merged 5 commits intocharkour:mainfrom
pstrassmann:pstrassmann-types-fix
Dec 23, 2023
Merged

fix equality fn type and update docs#144
charkour merged 5 commits intocharkour:mainfrom
pstrassmann:pstrassmann-types-fix

Conversation

@pstrassmann
Copy link
Collaborator

This PR updates the types as mentioned here.

I also included some updates to the docs. If you'd prefer I separate these docs updates into a separate PR, I'm happy to do so!

When I was first reading the docs for zundo there were some points of initial confusion for me, so some of my suggested updates are to clarify those things.

Thank you!

const { bears, increasePopulation, removeAllBears } = useStoreWithUndo();
// See API section for temporal.getState() for all functions and
// properties provided by `temporal`
const { undo, redo, clear } = useStoreWithUndo.temporal.getState();
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be personal preference, but I think it is simpler in the docs to introduce (and recommend) useStore.temporal.getState() prior to something like useTemporalStore. I think most users will not need to access member properties of the temporal object in a reactive way (that is, they'll likely just need undo, redo, clear and not need reactive access to pastStates or currentStates, isTracking, etc. I think introducing it this way may help facilitate a more straightforward mental model of what zundo is doing. But I'm not sure!

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with this, thank you for making this change and for providing an explanation!

@charkour charkour self-assigned this Dec 22, 2023
## Then bind your components

Use your store anywhere, including `undo`, `redo`, and `clear`!
) => useStore(useStoreWithUndo.temporal, selector, equality);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency in the docs, I replaced all references to stores created with temporal with useStoreWithUndo. I think this could makes things more clear to readers, instead of referring to the store by multiple names like useStoreWithUndo, originalStore, useStoreA, withTemporal, etc.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I didn't realize I used a different name each time. Oops!

);
```

If converting temporal store to a React Store Hook with typescript, be sure to define the type of
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is new -- giving an example of useTemporalStore with partialized state. If users useTemporalState<StoreState> while using partialize as a ZundoOption, they will see a type error. So this is to preempt that kind of error by providing an example of useTemporalState with a partalized store state typescript generic

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonderful!

README.md Outdated
`equality?: (pastState: PartialTState, currentState: PartialTState) => boolean`

For performance reasons, you may want to use a custom `equality` function to determine when a state change should be tracked. You can write your own or use something like [`fast-equals`](https://github.com/planttheidea/fast-equals), [`fast-deep-equal`](https://github.com/epoberezkin/fast-deep-equal), [`zustand/shallow`](https://github.com/pmndrs/zustand/blob/main/src/shallow.ts), [`lodash.isequal`](https://www.npmjs.com/package/lodash.isequal), or [`underscore.isEqual`](https://github.com/jashkenas/underscore/blob/master/modules/isEqual.js). By default, all state changes to your store are tracked.
Provide an `equality` function to determine when a state change should be tracked.
Copy link
Collaborator Author

@pstrassmann pstrassmann Dec 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed "performance reasons", as I think many reasons to use a custom equality function are not actually for performance reasons (although some are!). I think many instances of using an equality function are to ensure that effectively duplicate state histories are not tracked. For example, by using deep-equality or shallow equality fns. My impression is that the performance considerations may have more to do with which equality fn one chooses, as without one, history is naively added to no matter if shallow or deep equality is true.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation! Could you find a way to include possible reasons someone might use this option? I included performance originally because people asked for that reason and I wanted people to Cmd+F performance in the readme. However, I agree there are other important reasons too, so could you include more of them? Thank you!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem! I expanded the section a little bit, and added back the word performance for easy Cmd+F-ing -- let me know fi you think it is clear or if I should try to expand it more. I'm trying to find the right balance of not bloating the code examples while also trying to be as immediately clear to the reader as possible. If you think it would be even clearer to provide example StoreState interfaces for the various use-cases, I'm happy to add those. But if it seems to be sufficient without them, I think my instinct is to try to stay as lean as possible.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making that change! Striking the balance of keeping docs clear but bloated is difficult and I appreciate you for trying to find that balance too.

Sometimes multiple state changes might happen in a short amount of time and you only want to store one change in history. To do so, we can utilize the `handleSet` callback to set a timeout to prevent new changes from being stored in history. This can be used with something like [`throttle-debounce`](https://github.com/niksy/throttle-debounce), [`just-throttle`](https://github.com/angus-c/just/tree/master/packages/function-throttle), [`just-debounce-it`](https://github.com/angus-c/just/tree/master/packages/function-debounce), [`lodash.throttle`](https://www.npmjs.com/package/lodash.throttle), or [`lodash.debounce`](https://www.npmjs.com/package/lodash.debounce). This a way to provide middleware to the temporal store's setter function.

```tsx
const withTemporal = temporal<MyState>(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was initially confusing for me that these examples did not use create from zustand. I think it makes sense to keep a common code template for all ZundoOptions API examples, so this was updated here.


> While `setState`, `subscribe`, and `destroy` exist on `temporal`, you should not need to use them.

#### **React Hooks**
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was removed, as it seemed to be a duplicate of useTemporalStore example earlier in the docs

@charkour
Copy link
Owner

Overall these documentation updates are looking great. I'll give my complete review tomorrow. Thanks!

Copy link
Owner

@charkour charkour left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These updates are fantastic, thank you!

@charkour charkour merged commit 52f90e7 into charkour:main Dec 23, 2023
`zundo` has one export: `temporal`. It is used as middleware for `create` from zustand. The `config` parameter is your store created by zustand. The second `options` param is optional and has the following API.

### Middleware Options
### Bear's eye view:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This little easter egg is great!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha nice! I was hoping you'd like it 😆

@pstrassmann
Copy link
Collaborator Author

Thank you for reviewing and bringing in these changes!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants