-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
feat: add persist middleware #229
Conversation
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit cced1a6:
|
31e3798
to
27845ae
Compare
Thanks for opening up this. I'm happy to discuss and I also hope someone to chime in. First things first, I would like to factor out |
For sure it sounds great. I've already responded to someone asking how he could port this to |
Let me see, it shouldn't have to be a hook. export const persist = (
config,
name,
storage,
) => (set, get, api) => {
const store = config(
(payload) => {
set(payload) // payload can be a function
storage.setItem(name, JSON.stringify(get())
},
get,
api
)
(async () => {
const storedState = JSON.parse(await storage.getItem(name)) // needs error handling
api.setState(storedState);
})()
return store
} |
That's nice. How should the error handling be done? By logging a message with the |
Hm, don't know. It would be logged anyway. We need to think about the use case how we would recover from errors. |
Well, if an error is thrown during the parsing or the "getting" ( |
Are you sure? Not sure what is meant by "our try/catch". Anyway, it can be an edge case. We can start trying our basic idea. How do we proceed? Does my code snippet really work? Would you try? Would you incorporate it in this PR? |
(async () => {
try {
const storedState = JSON.parse(await storage.getItem(name))
api.setState(storedState);
} catch (e) {
// catches JSON.parse and storage.getItem errors
}
})()
I'll try it and if it does, I'll refactor the pr. |
How do you build the package so it can be linked? |
Just |
da8395a
to
1e35404
Compare
@dai-shi it seems to work https://codesandbox.io/s/react-typescript-forked-rj85m?file=/src/App.tsx (you can try by commenting / uncommenting the |
Confirmed. 👍 |
1e35404
to
4a68461
Compare
I don't know if this would bust the scope of this change/feature wide open (I do suspect that it does, though), but how do you feel about expressing changes as JSON Patches (considering that atomic updates to a zustand store may affect more than 1 key)? Having written this, I'm pretty sure that this would go beyond what you're trying to do here. I just wanted to point out that JSON Patch et al are a thing, and it would be great to have persistence glue based on it for zustand stores (it would allow some fun implementations of differential synchronisation.) If there is some interest in this stuff, I'd be happy to whip something up, as my current project is sort of heading into its general direction. |
4a68461
to
d380d41
Compare
@hmans differential synchronization sounds interesting. we can try that road if someone is interested, but it can be another middleware as you implied. This reminds me of something. How about factoring out JSON.parse/stringify and make them customizable? It should be easy if they are synchronous functions or even async doesn't matter. What do you think? @AnatoleLucet |
@dai-shi sure, why not. Do you think of a custom callback given through the arguments (then we might need to create an option object as the third argument of |
Maybe parse/stringify only. We don't need to cover 100% use cases. |
d380d41
to
e6d5d1b
Compare
There you go. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty good.
Feel like we should test it with RN. Any volunteers?
Should we have a small description in readme.md? |
e6d5d1b
to
46d7a2b
Compare
8a08f0b
to
7095c62
Compare
Now, after looking at the readme, I understand what this really means. |
I was just thinking about letting the user do this whole async function : ;(async () => {
try {
const storedState = await storage.getItem(name)
if (storedState) set(await deserialize(storedState))
} catch (e) {
console.error(new Error(`Unable to get to stored state in "${name}"`))
}
})() But you're right, this is probably too much customization. |
OK, we are on the same page. I'll think about again if we have a better option. I think our primary target is localStorage in web and AsyncStorage in RN. Meanwhile, would you check #229 (comment) again? It's not resolved. |
7095c62
to
cced1a6
Compare
Done |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
I don't know why codesandbox ci doesn't finish until now, but let me merge it since all other tests are passing. |
Thanks so much for adding this functionality! I had created my own wrapper for doing this in React Native & React Native Web. However, this is much more elegant! |
Hi 👋 I saw this comment so I've decided to open a pr for the middleware I've made and shared here.
I've been using it for multiples months on a project at work, and it actually works nicely given its simplicity.
But I still think there's some quite annoying drawbacks that we could probably improve: