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

deepObserve not working with ObservableSet #298

Open
gioragutt opened this issue Jun 27, 2021 · 4 comments
Open

deepObserve not working with ObservableSet #298

gioragutt opened this issue Jun 27, 2021 · 4 comments

Comments

@gioragutt
Copy link

repro:

  • repro.mjs
import {observable, runInAction} from 'mobx';
import {deepObserve} from 'mobx-utils';

const set = observable.set([1, 2, 3]);
const obj = observable({bla: true});

deepObserve(set, (change, path, root) => console.log(change, path, root));
deepObserve(obj, (change, path, root) => console.log(change, path, root));

console.log(set);

runInAction(() => {
  set.add('bla');
  obj.bla = false;
})

console output:

ObservableSet [Set] {
  name_: 'ObservableSet@1',
  data_: Set(3) { 1, 2, 3 },
  atom_: Atom {
    name_: 'ObservableSet@1',
    isPendingUnobservation_: false,
    isBeingObserved_: false,
    observers_: Set(0) {}, // <<<<<<<
    diffValue_: 0,
    lastAccessedBy_: 0,
    lowestObserverState_: 2,
    onBOL: undefined,
    onBUOL: undefined
  },
  changeListeners_: undefined,
  interceptors_: undefined,
  dehancer: undefined,
  enhancer_: [Function (anonymous)],
  [Symbol(mobx administration)]: {}
}

{
  type: 'update',
  observableKind: 'object',
  debugObjectName: 'ObservableObject@2',
  object: { bla: [Getter/Setter] },
  oldValue: true,
  name: 'bla',
  newValue: false
}  { bla: [Getter/Setter] }

Notice how in console.log(set) it has no observers.

@gioragutt gioragutt changed the title deepObserve not working with set deepObserve not working with ObservableSet Jun 27, 2021
@gioragutt
Copy link
Author

gioragutt commented Jun 27, 2021

Proposed change:

https://github.com/mobxjs/mobx-utils/blob/master/src/deepObserve.ts#L101

function observeRecursively(thing: any, parent: Entry | undefined, path: string) {
    if (isRecursivelyObservable(thing)) {
        const entry = entrySet.get(thing)
        if (entry) {
            if (entry.parent !== parent || entry.path !== path)
                // MWE: this constraint is artificial, and this tool could be made to work with cycles,
                // but it increases administration complexity, has tricky edge cases and the meaning of 'path'
                // would become less clear. So doesn't seem to be needed for now
                throw new Error(
                    `The same observable object cannot appear twice in the same tree,` +
                        ` trying to assign it to '${buildPath(parent)}/${path}',` +
                        ` but it already exists at '${buildPath(entry.parent)}/${entry.path}'`
                )
        } else {
            const entry = {
                parent,
                path,
                dispose: observe(thing, genericListener),
            }
            entrySet.set(thing, entry)
            entries(thing).forEach(([key, value]) => observeRecursively(value, entry, key))
        }
    // the else is added.
    } else if (isObservableSet(thing)) {
        const entry = {
            parent,
            path,
            dispose: observe(thing, genericListener),
        }
        entrySet.set(thing, entry)
    }
}

Tested and works on:

import {observable, runInAction} from 'mobx';
import {deepObserve} from 'mobx-utils';

const set = observable.set([1, 2, 3]);
deepObserve(set, (change, path, root) => console.log(change, path, root));
runInAction(() => set.add('bla'));

At first, I didn't add the isObservableSet(thing) check, and the following code failed:

import {observable, runInAction} from 'mobx';
import {deepObserve} from 'mobx-utils';

const obj = observable({
  bla: true,
  set: observable.set([1, 2, 3])
});

deepObserve(obj, (change, path, root) => console.log(change, path, root));
runInAction(() => obj.set.add('bla'));

With the following error:

/Users/gioraguttsait/Git/some-repo/node_modules/mobx/dist/mobx.cjs.development.js:91
    throw new Error("[MobX] " + e);
          ^

Error: [MobX] Cannot obtain administration from true
    at die (/Users/gioraguttsait/Git/some-repo/node_modules/mobx/dist/mobx.cjs.development.js:91:11)
    at getAdministration (/Users/gioraguttsait/Git/some-repo/node_modules/mobx/dist/mobx.cjs.development.js:5551:3)
    at observeObservable (/Users/gioraguttsait/Git/some-repo/node_modules/mobx/dist/mobx.cjs.development.js:3157:10)
    at Object.observe (/Users/gioraguttsait/Git/some-repo/node_modules/mobx/dist/mobx.cjs.development.js:3153:118)
    at observeRecursively (/Users/gioraguttsait/Git/some-repo/node_modules/mobx-utils/mobx-utils.umd.js:1195:35)
    at /Users/gioraguttsait/Git/some-repo/node_modules/mobx-utils/mobx-utils.umd.js:1188:32
    at Array.forEach (<anonymous>)
    at observeRecursively (/Users/gioraguttsait/Git/some-repo/node_modules/mobx-utils/mobx-utils.umd.js:1186:41)
    at deepObserve (/Users/gioraguttsait/Git/some-repo/node_modules/mobx-utils/mobx-utils.umd.js:1210:9)
    at file:///Users/gioraguttsait/Git/some-repo/mobile/test.mjs:9:1

@gioragutt
Copy link
Author

Another proposed solution:

https://github.com/mobxjs/mobx-utils/blob/master/src/deepObserve.ts#L32

function isRecursivelyObservable(thing: any) {
    return isObservableObject(thing) || isObservableArray(thing) || isObservableMap(thing) || isObservableSet(thing);
}

After thinking about it, an ObservableSet can also be recursively observable (since it can hold observables). Therefore it shouldn't be treated differently from the other types.

Needless to say that code that failed before now works

@urugator
Copy link
Contributor

urugator commented Jun 28, 2021

Does it work for non-stringable entries? Set's values are also it's keys, so I wonder what happens with path...

@gioragutt
Copy link
Author

Hmmm good point, I think that the entries in a Set are the items themselves as well, so that means that the path will probably be a toString of the observable item 🤭 I'll see how it looks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants