Infinite patch loop when applying reversePatch after a rejected request in onPatch #1682
-
I'm building an app with optimistic updates, and I'm using onPatch to send patches to the backend to sync my store with the backend. But this causes an infinite loop like this: You can see the example here: Update: import { render } from "react-dom";
import { types, onPatch, applyPatch } from "mobx-state-tree";
import { observer } from "mobx-react-lite";
const ScreenModel = types.model({
id: types.identifier,
name: types.string
});
const AppModel = types
.model({
screens: types.array(ScreenModel)
})
.actions((self) => ({
createScreen(screen) {
self.screens.push(screen);
},
removeScreen(screen) {
self.screens.remove(screen);
}
}));
const StoreModel = types
.model({
app: AppModel
})
.actions((self) => ({
removeScreen() {
self.app.removeScreen(self.app.screens[0]);
}
}));
const store = StoreModel.create({
app: {
screens: [
{
id: "1",
name: "Screen 1"
}
]
}
});
onPatch(store, async (patch, reversePatch) => {
// simulate sending the patch to the backend
new Promise((resolve, reject) => {
// fail to sync the patch
setTimeout(() => reject("fail"), 1000);
}).catch((error) => {
// apply the reverse patch patch to undo
// this will do on infinitely because
// applying reverse patch will trigger
// onPatch again
console.log(error);
applyPatch(store, reversePatch);
});
});
function App() {
return (
<div className="App">
<button
onClick={() => {
const count = store.app.screens.length + 1;
store.app.createScreen({
id: `${count}`,
name: `Screen ${count}`
});
}}
>
Add Screen {store.app.screens.length + 1}
</button>
<pre>{JSON.stringify(store, null, 4)}</pre>
</div>
);
}
const ObserverApp = observer(App);
const rootElement = document.getElementById("root");
render(<ObserverApp />, rootElement); |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
Hi @cdes! I'm not sure if there is a better way of fixing this, but I ran into this problem myself recently, and ended up temporarily disposing the patch listener, applying the reverse patch, and then started listening to patches again. In you example, that would be something like: let disposer: IDisposer;
function unlisten() {
disposer();
}
function listen() {
disposer = onPatch(store, async (patch, reversePatch) => {
new Promise((resolve, reject) => {
setTimeout(() => reject("fail"), 1000);
}).catch((error) => {
unlisten();
applyPatch(store, reversePatch);
listen();
});
});
}
listen(); |
Beta Was this translation helpful? Give feedback.
Hi @cdes!
I'm not sure if there is a better way of fixing this, but I ran into this problem myself recently, and ended up temporarily disposing the patch listener, applying the reverse patch, and then started listening to patches again.
In you example, that would be something like: