Skip to content
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

Persist middleware incompatible with Map and Set #618

Closed
nc opened this issue Oct 28, 2021 · 3 comments · Fixed by #1763
Closed

Persist middleware incompatible with Map and Set #618

nc opened this issue Oct 28, 2021 · 3 comments · Fixed by #1763
Labels
middleware/persist This issue is about the persist middleware

Comments

@nc
Copy link

nc commented Oct 28, 2021

Is this a KP? Wondering what the best workaround is? Even just importing persist seems to cause this.

@dai-shi dai-shi added the middleware/persist This issue is about the persist middleware label Oct 28, 2021
@nc
Copy link
Author

nc commented Oct 29, 2021

Will add repro steps shortly!

@nc
Copy link
Author

nc commented Oct 29, 2021

https://github.com/nc/repro-zustand-immer-persist

Wrote a simple Vite app to repro this issue.

Here's the main code:


import create from 'zustand';
import { persist } from 'zustand/middleware';
import produce, { enableMapSet } from 'immer';

enableMapSet();

type Store = {
  foo: Set<string>,
  addFoo: () => void
}

export const store = create<Store>(persist((set) => ({
  foo: new Set(),
  addFoo: () => {
    set(produce<Store>((state) => {
      state.foo.add('a foo');
    }))
  }
}), {
  name: 'persist-store'
}));

document.onreadystatechange = () => {
  store.getState().addFoo();
}

const app = document.querySelector<HTMLDivElement>('#app')!

app.innerHTML = `
  <h1>Hello Vite!</h1>
  <a href="https://vitejs.dev/guide/features.html" target="_blank">Documentation</a>
`

And the error:

    at main.ts:10
    at immer.js:669
    at e2.produce (immer.js:677)
    at immer.js:667
    at setState (zustand.js:1642)
    at zustand_middleware.js:212
    at Object.addFoo (main.ts:9)
    at HTMLDocument.document.onreadystatechange (main.ts:17)

It would seem all the Set, or Map methods are lost, perhaps it's being deserialized back to an Array or an Object instead of a Set or Map?

@AnatoleLucet
Copy link
Collaborator

AnatoleLucet commented Oct 29, 2021

By default, persist serializes the state using JSON.stringify.
Unfortunately JSON doesn't fit very well with Sets and Maps.
One way to fix this would be give a custom serialize & deserialize function to persist:

export const store = create<Store>(persist((set) => ({
  foo: new Set(),
}), {
  name: 'persist-store',
  serialize: (data) => {
    return JSON.stringify({
      ...data,
      state: {
        ...data.state,
        foo: Array.from(data.state.foo as Set<unknown>),
      },
    });
  },
  deserialize: (value) => {
    const data = JSON.parse(value);

    data.state.foo = new Set(data.state.foo);

    return data;
  },
}));

@AnatoleLucet AnatoleLucet changed the title Persist middleware incompatible with immer enableMapSet. Persist middleware incompatible with Map and Set Nov 2, 2021
advename added a commit to advename/zustand that referenced this issue Aug 29, 2022
As seen in:
- pmndrs#652 
- pmndrs#1143 
- pmndrs#457
- pmndrs#618 

The documentation lacks an example that would both explain and highlight actions and nested objects in persisted stores.

Personally, I had to use more time than I hoped for to find [this comment](pmndrs#1143 (comment)) in an issue, instead in the docs. The docs has all the puzzle pieces - but requires larger understanding of how these pieces work together. With this section using information from above outlined issues, I hope to make things a bit more clear.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
middleware/persist This issue is about the persist middleware
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants