Search code examples
reactjsmobxmobx-react

How do I subscribe to @computed object in mobx?


I want to subscribe to a computed property that returns a simple object.

Codesandbox: https://codesandbox.io/s/epic-noether-g8g0x

I made a simple example of my situation to show exactly what the problem is:

const zeroSize = {
    width: 0,
    height: 0
};

class Item {
    @observable size = zeroSize;
}

class Collection {
    @observable childrens: Item[] = [];

    @computed get size() {
        return this.childrens.length > 0 && this.childrens[0] ? this.childrens[0].size : zeroSize;
    }

    constructor() {
        reaction(() => this.size, size => {
            // Expected { width: 0, height: 1000 }
            // Actual { width: 0, height: 0 }
            console.log('-----size=', toJS(size));
        });
    }
}

const item1 = new Item;
const collection1 = new Collection;
collection1.childrens.push(item1);
item1.size.height = 1000;

https://github.com/mobxjs/mobx/issues/2176


Solution

  • You are listening for the wrong change.

    Because you are listening for changes to size, the height will trigger a change:

    const item1 = new Item; // size === 0
    const collection1 = new Collection; // size === 0
    collection1.childrens.push(item1); // size === 1 (triggers reaction)
    item1.size.height = 1000; // size === 1 (doesn't trigger reaction)
    

    If you want to listen to any and all changes that are the result of childrens changing, then that is what you need to be listening to changes for.

    reaction(
      () => toJS(this.childrens),
      size => {
        // Expected { width: 0, height: 1000 }
        // Actual { width: 0, height: 0 }
        console.log("-----size=", toJS(this.size));
      }
    );
    

    By listening to toJS(this.childrens), you can subscribe to when the length of childrens changes, but also when a property of an element of childrens changes.

    If you only want to listen to when new children are added or removed, then listening to size make sense. If you want to listen to when children are added, removed or modified, then you need to change what you are listening to.

    Here is a sandbox: https://codesandbox.io/embed/white-hill-fntxt

    And the relevant console logs:

    -----size= Object {width: 0, height: 0}
    -----size= Object {width: 0, height: 1000}