You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
So far I am loving this library but recently noticed an issue when attempting to sync data between 2 or more clients. Using mobx and adding or removing from an array causes related computed properties to invalidate for every element in the array. I have written some tests and put them in a branch.
Interestingly, the yjs-reactive-bindings don't seem to be at fault. If I don't use SyncedStore with mobx and only use observeYJS from the yjs-reactive-bindings package then the computed properties fire the expected number of times (once per push/remove from the array).
I will also inline the code here if that is more convenient:
import{syncedStore,enableMobxBindings}from'@syncedstore/core';import{observeYJS}from'@syncedstore/yjs-reactive-bindings'import*asmobxfrom'mobx';import*asYfrom'yjs';enableMobxBindings(mobx);typeYjsThing={id: string;};constcreateSyncedStore=(doc: Y.Doc)=>syncedStore({things: []asYjsThing[]},doc);classThingSyncStore{doc: Y.Doc;store: ReturnType<typeofcreateSyncedStore>;constructor(doc: Y.Doc){this.doc=doc;this.store=createSyncedStore(doc);mobx.makeAutoObservable(this,{doc: false,store: false,});}getthingsMap(){returnthis.store.things.reduce((acc,thing)=>{acc[thing.id]=thing;returnacc;},{}asRecord<string,YjsThing>);}addPoint(id: string){this.store.things.push({
id,});returnid;}}describe('yjs syncedstore with mobx binding',()=>{it('emits one change for each push to an array',async()=>{constdoc=newY.Doc();conststore=newThingSyncStore(doc);constautorunSpy=jest.fn();mobx.autorun(()=>{console.log(Object.keys(store.thingsMap).length);autorunSpy();});// when working on the store locally, we expect the autorun to fire once// for the things array being updatedautorunSpy.mockClear();store.addPoint('1');expect(autorunSpy).toHaveBeenCalledTimes(1);autorunSpy.mockClear();store.addPoint('2');expect(autorunSpy).toHaveBeenCalledTimes(1);autorunSpy.mockClear();store.addPoint('3');expect(autorunSpy).toHaveBeenCalledTimes(1);// create a new doc which we will pretend is another client we wish to syncconstdoc2=newY.Doc();Y.applyUpdate(doc2,Y.encodeStateAsUpdateV2(doc));// apply updates from doc2 to docdoc2.on('update',(update)=>{Y.applyUpdate(doc,update);});constthings=doc2.getArray('things');autorunSpy.mockClear();// This is where the problem is, the autorun is firing once for the change// to the things array, but also once for each element in the array. If you// add 10 things, the autorun will fire 11 times. This becomes a huge problem// when you want to have multiplayer with a large number of items in the array.constthing=newY.Map();thing.set('id','4');things.push([thing]);expect(autorunSpy).toHaveBeenCalledTimes(1);});});classThingYjsStore{doc: Y.Doc;constructor(doc: Y.Doc){observeYJS(doc);this.doc=doc;}getthings(){returnthis.doc.getArray<Y.Map<any>>('things');}getthingsMap(){constthingMap: Record<string,Y.Map<any>>={};this.things.forEach((thing: Y.Map<any>)=>{constid=thing.get('id');thingMap[id]=thing;});returnthingMap;}addPoint(id: string){constthing=newY.Map();thing.set('id',id);this.things.push([thing]);returnid;}}// when use only use the yjs reactive bindings then these tests passdescribe('yjs syncedstore without mobx binding',()=>{it('emits one change for each push to an array',async()=>{constdoc=newY.Doc();conststore=newThingYjsStore(doc);constautorunSpy=jest.fn();mobx.autorun(()=>{console.log(Object.keys(store.thingsMap).length);autorunSpy();});// when working on the store locally, we expect the autorun to fire once// for the things array being updatedautorunSpy.mockClear();store.addPoint('1');expect(autorunSpy).toHaveBeenCalledTimes(1);autorunSpy.mockClear();store.addPoint('2');expect(autorunSpy).toHaveBeenCalledTimes(1);autorunSpy.mockClear();store.addPoint('3');expect(autorunSpy).toHaveBeenCalledTimes(1);// create a new doc which we will pretend is another client we wish to syncconstdoc2=newY.Doc();Y.applyUpdate(doc2,Y.encodeStateAsUpdateV2(doc));// apply updates from doc2 to docdoc2.on('update',(update)=>{Y.applyUpdate(doc,update);});constthings=doc2.getArray('things');autorunSpy.mockClear();constthing=newY.Map();thing.set('id','4');things.push([thing]);expect(autorunSpy).toHaveBeenCalledTimes(1);});});
The text was updated successfully, but these errors were encountered:
Hi,
So far I am loving this library but recently noticed an issue when attempting to sync data between 2 or more clients. Using mobx and adding or removing from an array causes related computed properties to invalidate for every element in the array. I have written some tests and put them in a branch.
Interestingly, the
yjs-reactive-bindings
don't seem to be at fault. If I don't use SyncedStore with mobx and only useobserveYJS
from theyjs-reactive-bindings
package then the computed properties fire the expected number of times (once per push/remove from the array).here is the code to reproduce this isssue:
https://github.com/YousefED/SyncedStore/compare/main...BradHarris:SyncedStore:bradharris/issue-122?expand=1
I will also inline the code here if that is more convenient:
The text was updated successfully, but these errors were encountered: