Mobx: observable.array({deep: false}) not behaving the same as @observable.shallow

Created on 9 Apr 2018  ·  5Comments  ·  Source: mobxjs/mobx

I have a simple store that has the following structure:

class SearchStore {
    @observable term = 'javascript';
    @observable state = '';
    results = observable.array([], {deep: false});

    @computed
    get isEmpty() {
        return this.results.length === 0;
    }

Here the computed property isEmpty() is never triggered if I set results to a new value. In fact, the second time I set results, it turns into a regular array and is no longer observable.

    @action.bound
    async search() {
        /* ... */
       this.results = searchResults;
    }

However, if I declare results with a decorator:

@observable.shallow results = []

it works!. Is my usage of observable.array() not correct?

❔ question

Most helpful comment

@pavanpodila the difference is that the first creates an observable _array_, and the second an _observable_ property, that will converts it value to an observable automatically if possible.

So this.results = does not change the array itself, and hence it will not be picked up if only the array was made observable. this.results modifies this instead, so you need the property to be observable to pick this up.

In general, I recommend to either use:

@observable.shallow results = []
// or
readonly results = observable.array([], {deep: false});

The readonly modifier (in typescript) will protect you against accidentally modifying the non-observable property, where a change to the obsrevable array was intended.

@observable.shallow is not internally using .replace; assigning a new value will assign a new array instance to the array property, not recycling the existing one. So purely theoretically speaking readonly results is slightly faster and safer then @observable.shallow; as the latter has two changing pieces (the object property and the array) and the first only one.

All 5 comments

Just found out that I need to use the replace() API of the observable.array() to make it work properly.

@mweststrate, Looks like @observable.shallow is internally using that, I guess?

@pavanpodila the difference is that the first creates an observable _array_, and the second an _observable_ property, that will converts it value to an observable automatically if possible.

So this.results = does not change the array itself, and hence it will not be picked up if only the array was made observable. this.results modifies this instead, so you need the property to be observable to pick this up.

In general, I recommend to either use:

@observable.shallow results = []
// or
readonly results = observable.array([], {deep: false});

The readonly modifier (in typescript) will protect you against accidentally modifying the non-observable property, where a change to the obsrevable array was intended.

@observable.shallow is not internally using .replace; assigning a new value will assign a new array instance to the array property, not recycling the existing one. So purely theoretically speaking readonly results is slightly faster and safer then @observable.shallow; as the latter has two changing pieces (the object property and the array) and the first only one.

@mweststrate Thanks for the explanation. Is it right to say that the same behavior could be achieved if I had used the decorate() API?

decorate(SearchStore, { results: observable.shallow })

@pavanpodila yes

Awesome. Thanks Jiri

Regards,

--
Pavan Podila

On 9 Apr 2018, 12:56 PM +0530, Jiri Spac notifications@github.com, wrote:

@pavanpodila yes

You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

Was this page helpful?
0 / 5 - 0 ratings