-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Persisted state gets thrown away if any state is set in useEffect #562
Comments
Is this an issue only related with SSR? (In other words, is it reproducible with a client-only example?) |
Thanks for the fast response! The issue does not seem to be with SSR. I just spun up a quick reproducible case with create-react-app and the issue is still there. Here's the repo: https://github.com/churichard/zustand-persist-bug Click the button to increase the number of bears (which is stored in IndexedDB). Once you refresh the page, the bears resets to 0. Comment out the |
Thanks for the reproduction! This should help. |
Not sure how this should be fixed, or even if it should be. Changing the current behavior to something "smarter" might cause more trouble than leaving it that way. For now, you should be able to fix it by setting the state once the store has been hydrated. You can do that with something like this: #346 (comment) useEffect(() => {
if (hasHydrated) {
increaseBirds();
}
}, [increaseBirds, hasHydrated]); |
Or maybe we delay the |
I think I understand set state is invoked before hydration, but why is the state not overwritten by the storage value? |
If I understand correctly, this is happening because:
Would it be possible to not persist any new data when the previous persisted data has not be read yet? If we can do that, then the |
Correct!
Yeah, that's what I meant with this comment #562 (comment) |
I think what you said was slightly different. What I mean is for the initial state to actually be updated in the My use case is that I want to actually dynamically set the initial state. So there's some properties that can't be determined at build time (like whether the user is on a mobile device), and I want to change the default values based on that. But I want whatever is persisted in IndexedDB to always override whatever state was set by default or in the If that's actually what you meant, sorry 😅. I interpreted it as actually not setting any state at all until IndexedDB is read, which would mean that the |
I see it overwrites data in the storage, before reading. How does this hack fix, or not? let initialized = false;
const storage = {
getItem: async (name) => {
const item = await localforage.getItem(name);
initialized = true;
return item;
},
setItem: async (name, value) => {
if (!initialized) {
console.warn('not ready yet');
return;
}
await localforage.setItem(name, value);
}
}; |
Indeed I misunderstood 😄 This might be a better and more predictable default.
Should work. Though, if there's no |
I agree! There are already good options for overriding what's been persisted, but no good options for setting state before the persisted data is loaded. Plus, I think this is intuitively how people would think it would work if the
I just tried this out, but setting the state in
For now, I'm using this workaround. It's not perfect because it runs in the opposite order of which I want, so I need to use some heuristics to determine whether I should be keeping or throwing away the hydrated state. But it's a decent temporary solution 😄 |
@AnatoleLucet Should we close this issue or keep it? (Can you check all other issues for middleware/persist too?) |
Closing as a workaround is provided. |
the workaround is brittle |
I'm having a strange bug where my persisted state is getting overridden.
I'm using Next.js, Zustand with the persist middleware, and localForage using async IndexedDB. I have default values set in my store, but when my app first runs I want to use some different default values in a
useEffect
depending on whether the user is using a mobile device or not.According to my mental model of Zustand, what should happen is that the
useEffect
runs before or after the persisted state is loaded. If it runs before, the persisted state should get merged in with priority. If it runs after, the persisted state should be merged in with the properties inuseEffect
having priority. What I'd actually like to happen is for theuseEffect
to get run before, but it doesn't matter for the purposes of this issue.Instead, what seems to be happening is that all of my persisted properties get thrown away, and I get the default values along with the values I set in the
useEffect
. This doesn't make sense to me, and it seems like a bug. My persisted properties should always stay persisted if I haven't explicitly overridden them, which in this case I haven't -- all I've overridden are some other properties.If I comment out the
useEffect
, then it works as you would expect: the persisted state stays persisted.Hopefully this makes sense. If you'd like to take a look at my code to get a better idea of what I'm talking about, the store is here and the code that runs in the
useEffect
is here.Edit:
Minimal reproducible case: https://github.com/churichard/zustand-persist-bug
Click the button to increase the number of bears (which is stored in IndexedDB). Once you refresh the page, the bears resets to 0.
Comment out the
useEffect
to see it work fine and for the number of bears persist.The text was updated successfully, but these errors were encountered: