-
-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Updating the value of an untracked value prior to updating a tracked value results in pastStates not updating when using handleSet (Sandbox example) #139
Comments
Thanks for the kind message and for the reproduction, @pstrassmann! I'll have to take a look later to determine what's happening. What you're describing sounds like a bug, but I'll need to see what's happening internally when triggering changes. So far, I don't have a good workaround. |
@charkour Thank you for taking a look! It is very much appreciated! I don't know how helpful this is, but in my initial attempts to debug this, the issue appears to possibly be (proximally) caused by the equality function receiving a stale value for Because the Here is a sandbox with this (ever so slightly further) debugging: And a code example showing the minor debugging attempt (see {
equality: (pastState, newState) => {
console.log("pastBears: ", pastState.bears);
console.log("newBears: ", newState.bears);
console.log("areBearsEqual: ", pastState.bears === newState.bears); // true
return deepEqual(pastState, newState);
},
handleSet: (handleSet) =>
throttle<typeof handleSet>((state) => {
console.log("handleSet called");
handleSet(state);
}, 500),
partialize: (state: MyState): HistoryTrackedState => {
const { untrackedValue, ...trackedValues } = state;
return { ...trackedValues };
},
}, Thank you again!! |
Hi @pstrassmann, I'm working to debug this now. I'm able to reproduce the issue on your sandbox (thank you for creating that!!) and I'll let you know what I find. |
@charkour Thank you so so much. I've pulled down the repo and have it linked locally and have been trying to debug a little bit. I've made a small amount of progress -- I actually think it has nothing to do with partialize (or tracked or untracked values), but instead that the So for example, if this callback is called on a button click: const handleClick = () => {
incrementBears()
incrementCats()
} the See this simplified sandbox if helpful Thank you again. |
Thanks, @pstrassmann, that is helpful context! Is it possible to do something like this? Combining the setters into one action could solve the issue. In your latest sandbox, I no longer see the bug, however. const handleClick = () => {
incrementBearsAndCars()
} |
@pstrassmann, I'm not sure of your usecase in production, but is a valid workaround combining the tracked value and untracked value into one setter function? I forked the second codesanbox here to add the In the most recent Code Sandbox you shared, I don't see the bug. Thanks for the help on this! |
It seems like the beta codesandbox isn't working well... I've tried to update the comment to link here: https://codesandbox.io/p/sandbox/zundo-with-partialize-and-deep-equal-debugging-forked-22v9ms |
Hi @charkour -- Thank you for bearing with my as I get a better hold of my issue. Please disregard the last Sandbox I sent you, I was confused. Unfortunately the combined setter doesn't work for us (described more in detail below) due to users being able to modify state in different places in the application all within the throttle interval. The TL;DR of my issue is: Is there a way to conditionally trigger a More detail: To summarize, the issue I'm having is that when using both However, in our application, users may perform actions in various places that change untracked and tracked values, making it unrealistic to combine these actions into a single state setter as you suggested like As a contrived example, take two buttons const handleButtonClickA = () => {
incrementUntrackedValue()
}
const handleButtonClickB = () => {
incrementTrackedValue()
} Sandbox for above example: https://codesandbox.io/p/sandbox/zundo-untrackedvalue-wq9cm8?file=%2Fsrc%2FApp.tsx%3A63%2C11 If ButtonA is clicked and then ButtonB is clicked all within the throttle interval, the change to the trackedValue will not be registered in history, as ButtonA initiated the throttle interval. A potential solution to my problem could be to make // This would never initiate the throttle interval
const handleButtonClickA = () => {
incrementUntrackedValue()
}
// If clicked immediately after ButtonA, this would be registered in history and throttle interval initiated.
const handleButtonClickB = () => {
incrementTrackedValue()
} I'm still chewing on this one, but if you have any insight for how to do this -- or a potential change to a forked version of zundo, it would be much appreciated! To achieve this, I'm wondering if the Thanks so much for taking the time to help with this! |
Hi @charkour . I think I've identified a fix to the issue -- I included the details in a PR. If you could take a look it would be most appreciated! The general idea is: Currently handleSet is called even if (history tracked) state hasn't changed. That is, handleSet is called after any change to the zustand store. This PR changes this behavior so that handleSet is only called if history tracked state has changed. For consistency, the |
Hey @pstrassmann, thank you for the detailed work on this! I'll take a look at the PR. Thanks again. For the time being, do you have a suitable workaround? |
Thank you for taking a look! Unfortunately we don’t have an alternative work around at the moment that uses ‘zundo’ (but we also don’t have an immediate alternative without ‘zundo’, without forking). I’ll keep working on this however — appreciate all the help! |
Sounds good, for the time being, you could use something like patch-package (if you're using npm). Yarn and Pnpm have other patching solutions. |
Ah that’s wonderful, thank you for that! |
Is patching the package locally a suitable workaround for now as we determine the best way to fix this issue? Thank you |
I'll work on patching the package locally today. I believe patching should be a suitable workaround in the short term, thank you for that! In the meantime, I'm happy to address any feedback big or small related to either of the potential solutions in the PRs. Also, if you'd prefer to implement an alternative solution yourself or reimplement one of my solutions to be more in line with your stylistic preferences, that is of course also okay! Take whatever time you need to feel comfortable with a solution. Thank you for your help! |
Thanks for being flexible and willing to work on solutions! I wish I could work on this full time haha Are you using zundo in prod or in a personal project? |
We are looking at adding zundo in prod to a currently live application at Classy! |
Just adding some additional thoughts about the solutions for potentially helpful context during review:
For example, with PR 142, the user would not need to provide a diff nor equality fn if they write their handleSet to only trigger according to an equality fn within handleSet: (handleSet) => {
const throttleFn = throttle((state) => {
handleSet(state); }, 1000, );
return ( pastState, currentState ) => {
if (isDeepEqual(pastState, currentState) {
throttleFn(pastState);
}
}; With the above, the cool-off and state history setting always remain in sync (assuming no additional However, if the user additionally provided an |
I'm glad you're considering using this in production! It's nice to see the ecosystem growing around small but performant state solutions. Thanks for the additional context; I'm currently testing out some potential work around, but I'm leaning towards merging in PR #141 to avoid an overly complicated API as you stated. Thank you for the great help on this! |
To close the loop, what you reported is a bug (and oversight in my initial implementation) that can be fixed via #141. Thanks for reporting this! |
Thanks @charkour! I made some minor changes based on your feedback to PR 141. The main new change is to dry up the conditional calling of After some noodling, I do not currently think it is possible to pass additional parameters to However, I also don't think that the API changes required to be able to pass additional paramaters to I know it's the holidays so no rush and please take your time, and thanks again for your help with all of this! |
Hey @pstrassmann, thank you for the awesome work on both the bug write-up and creating multiple PRs. This is what makes open source so awesome. I hope you had a nice holiday too. As for version changes, I recommend checking out the Semver docs. In short, if we are adding features (such a more parameters in a callback) then it's a minor version change, but if we remove or change parameters or the external behavior, then it's a major version change. For small fixes, it would be a patch. I have my gripes with semver (I wish it were at least four numbers, not three), but it's what the industry uses :) Thanks for being conscious of avoiding breaking changes! Happy to chat more about this if you have more questions. Thank you |
This has been resolved in #149 I'll release v2.1.0 shortly. |
This was fixed in v2.1.0 |
Hello! First, thank you so much for your work on this -- it is amazing!
I noticed that changing the state value of an untracked value prior to changing the state value of a tracked value results in pastStates not getting updated when using handleSet. However, doing this the other way around has no issues (updating the state value of a tracked value prior to updating the state value of an untracked value).
Sandbox: https://codesandbox.io/p/sandbox/zundo-with-partialize-and-deep-equal-xnjwx4?file=%2Fsrc%2FApp.tsx%3A52%2C5
I'm using
partialize
to not track a certain value.I'm using
handleSet
to throttle (with just-throttle)I'm using
equality
for deep-equal comparison (fast-deep-equal)I'm not 100% sure if this is a bug or if I am missing something. Do. you have any insight?
Thank you so much!
The text was updated successfully, but these errors were encountered: