xaviergonz / mobx-keystone

A MobX powered state management solution based on data trees with first class support for Typescript, support for snapshots, patches and much more

Home Page:https://mobx-keystone.js.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Root Registry / Backrefs with deep observation

PEZO19 opened this issue · comments

Rationale:
It would be really nice to get updated about all changes (also deep updates) of all instances of a given Model in a canonical way "at a central place".

As I understand from keystone docs and the implementation, getRefsResolvingTo gives you back a standard mobx ObservableSet, which does not seem to support deep observation (by default). It would be nice to get updated about the deep changes of this ObservableSet. Is it a preferred/canonical way to do it in keystone? Is it preferred to use it with mobx-utils' deepObserve? I myself did not try it out yet, but have found this Open issue: deepObserve not working with ObservableSet. I also mind updateAllRefsIfNeeded=true case.

Am I missing something?

you can use a plain mobx reaction with getSnapshot to do it (since snapshots create a new ref any time anything inside that snapshot mutates)

reaction(() => getRefsResolvingTo(x).map(ref => getSnapshot(ref.current)), () => {
  // this would trigger every time the set or any model in the set deeply mutates
});

@xaviergonz I see, however this does not provide me the changes (and path) inside this (deeply) observed observable, right? How could I get "path" and "changes" like this below?

deepObserve(target, (change, path) => {
   console.dir(change)
})

Sorry, maybe nested onPatches is totally fine for my use case (without deep observation), let me get back here later. Wondering if there is a pattern/antipattern related to this.

Just for the record, I want to do something very similar proposed by @xaviergonz here: #436 (comment)

However my current issue is that I can't really setup nested onPatches, because in case of an outer add Patch, I can't setup the inner onPatches, because objectToAdd is not part of the tree yet. See below.

onPatches(collection, (collectionPatches, collectionInversePatches) => {
    collectionPatches.forEach((collectionPatch) => {
      if(/*it is an "add" Patch I'm interested in*/) {

        // I'd like to setup a "deep observation"
        const objectToAdd = ... // we can get it from collectionPatch (eg. via path)
        onPatches(objectToAdd, ...)
        //             ^ gives this error:
        //             subtreeRoot must be a tree node (usually a model or a shallow / deep child part of a model 'data' object)
      }
      // ...
})})

I assume, instead nested onPatches I have to use nested "raw" mobx observe calls for that, right?

Is there any important downside of doing that / any gotchas you are aware of (in context of using keystone that way) ?

Root registry with observation:
My current approach is maintaining a single ObjectMap registry per keystone Model and using mobx observe on ThingObjectMapRegistry.items.$ (instead relying on onPatches which was mentioned by me above, because onPatches fires at the end of the transaction and is too late for me).

Root registry with deep observation:
(multiple Registries, items referencing to each other eg.: "pet" in "PetRegistry" has a reference on "Owner" which lives in "OwnerRegistry")
Using the basic idea of mobx-utils deepObserve, I am setting up these "deepObserve(AtPath)" chains by hand using the .$ observable part as mentioned above.

About refs/backrefs:
If refs are going to be needed, I think I'll use a customRef which uses that thingObjectMapRegistry.
For backrefs, I have realized, that I can not/ do not want to use getRefsResolvingTo, for complex cases it is not powerful enough and explicit modeling of references is needed. (Eg. if I am interested "how/where" the ref source uses the ref.)

These might not really be the scope of keystone and the use cases are really custom and complex so I close the issue, but if anyone has comment on these, I'd appreciate it.