-
Notifications
You must be signed in to change notification settings - Fork 12
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
Yjs Middleware Does Not Work with Immer Middleware #53
Comments
I notice in your example that you are using the Zustand vanilla The vanilla Are you using the vanilla |
Yes that's exactly what I have for the store. What should I use instead? And is that the correct way of using immer? I added immer because I want to be able to change node fields directly (like node.field=x) and re-render automatically. I am understanding correctly? Thanks! |
To get React to update whenever the Zustand store changes, you should just use That should fix your problem. Middlewares work the same regardless of which version of |
If I use that code without vanilla and without immer, react still doesn't refresh until I reload the tab. But that may be something I did wrong in react flow. If I use immer I get this error calling the addNode, on the client that syncs (I don't get the error on the client that calls it initially):
Even if I use my old immutable methods I get the same error. const store = createStore(
immer(yjs(
ydoc,
docName,
(set) =>
({
nodes: [],
edges: [],
addNode: (node: any) => {
set((state: any) => ({
...state,
nodes: [...state.nodes, node]
}))
},
removeNode: (id: any) => {
set((state: any) => ({
...state,
nodes: state.nodes.filter((node: any) => node.id !== id)
}))
},
addEdge: (edge: any) => {
set((state: any) => ({
...state,
edges: [...state.edges, edge]
}))
},
removeEdge: (id: any) => {
set((state: any) => ({
...state,
edges: state.edges.filter((edge: any) => edge.id !== id)
}))
}
})
))
); |
OK that is definitely an error in the Yjs middleware. Looking at the code in question in the Yjs middleware, it received an Immer immutable array (created on store initialization) and then tried to call I'm going to need to do some digging to figure out what exactly the sequence of events are when the Yjs and Immer middlewares are composed to together. In the meantime, try switching the order of the Yjs and Immer middlewares in your code, i.e.: const store = createStore(yjs(immer(...))); (This should not change the result, as middleware should be commutative (order does not matter).) |
Hey @joebobmiles, running into the same issue. Did you ever get around to looking into this? |
I... Yes? Do you not see the linked PR? The two linked commits? That out of the way, I did forget about this issue. Looking at the errors from the failed tests on the PR, I am reminded that this bug will be a pain to resolve. Immer expects that a producer (the code that mutates an immutable value) either returns the new value for Immer to swap or mutates a reference to that value which Immer provides to the producer. The Yjs middleware violates this because of the two-way binding it creates between Zustand and the Yjs store. What happens is that when a value is modified locally via Zustand, that change is sent to Yjs in a transaction. Yjs evaluates the transaction, but because the Yjs store changed, it sends an 'echo' of the change it received back to Zustand. If unchecked, this would cause an infinite loop, but in this case, Yjs is using a version of Zustand's set that hasn't been augmented by the middleware, avoiding Zustand sending a duplicate change. But this results in a problem: Zustand updated its state twice during a single set operation, once in a "legal" way and once in an "illegal" way, as far as the Immer middleware is concerned. But the only way around this is to, more or less, break the two-way binding between Zustand and Yjs, which breaks the core functionality of the Yjs middleware. So I have to re-evaluate and rewrite how the two-way binding works just to appease Immer. Which is a pain. |
I did see the PR and the tests but more around if you had been able to get any further since then 😄 That was perhaps lost in translation of my British-ness, sorry! I appreciate your hard work - that does sound like a pain. I think what I'll do then is just quit using Immer. Thanks for the extra explainer! |
I am trying to use this to sync a react flow store but when yjs syncs react doesn't refresh the page from zustand contents.
I tried to add immer but still not working
here is my store:
The text was updated successfully, but these errors were encountered: