Mobx: How to track object mutations?

Created on 17 Nov 2016  Â·  11Comments  Â·  Source: mobxjs/mobx

Example with Moment.js:

var person = mobx.observable({
  birthday: moment()
});

mobx.autorun(function() {
  console.log('autorun: ' + person.birthday.toString());
});

person.birthday = moment(person.birthday).add(1, 'day'); // Ugly but works
person.birthday.add(1, 'day'); // Does not work

console.log('no autorun: ' + person.birthday.toString());

Output:

autorun: Thu Nov 17 2016
autorun: Fri Nov 18 2016
no autorun: Sat Nov 19 2016

Online jsFiddle: https://jsfiddle.net/tkrotoff/ek64vvtf/

autorun is not being called when running person.birthday.add(1, 'day').
Moment.add mutates birthday and Mobx does not know about it.

How can it be done elegantly?

PS: I've spent a long time in my code base before understanding why it did not work as I expected, I suggest to document this to avoid others the same mistake

Most helpful comment

@mattruby
From the timer example, first initialisation: startTime: null then later startTime = moment().
Since MobX tracks property access, not values it works.

In other words (tell if wrong), Mobx tracks the pointer value (pointer as in C/C++ pointer).

That's why person.birthday.add(1, 'day') does not work: changing the value, not the pointer.
And person.birthday = moment(person.birthday).add(1, 'day') works: changing the pointer.

All 11 comments

Check out my timer examples, https://github.com/mobxjs/mobx-examples . They
were using moment at one time. You'll want to use a computed to show the
date output. It should trigger as you'd like.

On Nov 17, 2016 4:39 PM, "Tanguy Krotoff" [email protected] wrote:

Example with Moment.js:

var person = mobx.observable({
birthday: moment()
});
mobx.autorun(function() {
console.log('autorun: ' + person.birthday.toString());
});
person.birthday = moment(person.birthday).add(1, 'day'); // Ugly but worksperson.birthday.add('1', 'day'); // Does not work
console.log('no autorun: ' + person.birthday.toString());

Output:

autorun: Thu Nov 17 2016
autorun: Fri Nov 18 2016
no autorun: Sat Nov 19 2016

Online jsFiddle: https://jsfiddle.net/tkrotoff/ek64vvtf/

autorun is not being called when using person.birthday.add('1', 'week').
Moment.add http://momentjs.com/docs/#/manipulating/add/ mutates birthday
and Mobx does not know about it.

How can it be done elegantly?

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/mobxjs/mobx/issues/662, or mute the thread
https://github.com/notifications/unsubscribe-auth/AAIrcj9svjmypNDHZB4D3RtH9rtLNJVvks5q_NdVgaJpZM4K15jw
.

@tkrotoff, do you mean documenting how mobx reacts?

@mattruby
From the timer example, first initialisation: startTime: null then later startTime = moment().
Since MobX tracks property access, not values it works.

In other words (tell if wrong), Mobx tracks the pointer value (pointer as in C/C++ pointer).

That's why person.birthday.add(1, 'day') does not work: changing the value, not the pointer.
And person.birthday = moment(person.birthday).add(1, 'day') works: changing the pointer.

@tkrotoff Actually it's Date who basically causes this confusion. Moment.js uses Date instance to store the exact value, while mobx doesn't track change of Date instance - which is difficult.

You can try this example and you'll find that mobx tries to make object content observable recursively: https://jsfiddle.net/nbk3peoa/

@mweststrate Maybe mobx can do some monkey patch stuff to make Date instance changes trackable? Just like what mobx did with Array instances.

@tkrotoff What ended up being the elegant solution to this? Tearing my hair out a bit...

I'm curious about that too because I have the same problem. Is there some more elegant solution?
Thanks

btw, will ES6 Proxy be able to solve this problem?

@kpennell @francesco-carrella I recommend to treat the date as immutable, and every time you want to modify it, produce a new data and store that in a observable.ref

Thanks for the support @mweststrate, I ended using this way out.

Replying to my question "will ES6 Proxy solve this problem?", the answer is no. See https://codepen.io/tkrotoff/pen/WyWOZP

Was this page helpful?
0 / 5 - 0 ratings