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

extendsObservable doesnt trigger re-render #1038

Closed
janhoeck opened this issue Jun 9, 2017 · 10 comments
Closed

extendsObservable doesnt trigger re-render #1038

janhoeck opened this issue Jun 9, 2017 · 10 comments

Comments

@janhoeck
Copy link

janhoeck commented Jun 9, 2017

Hello everybody,

I known there are a lot of closed tickets with the same topic.
(See: #194)

All tickets got closed with the answer: Use observable.map!

But why? The documentation says for "Observable Objects":

When passing objects through observable, only the properties that exist at the time of making the object observable will be observable. Properties that are added to the object at a later time won't become observable, unless extendObservable is used.
(https://mobx.js.org/refguide/object.html)

Sure I could use observable.map but there I dont have the nice dot notation for accessing properties in javascript. And this is what I want, because this is my code convention.

My question now is: Do the documentation tells wrong information or is there a way to get extendsObservable work?

@mweststrate
Copy link
Member

mweststrate commented Jun 9, 2017

ExtendsObservable can add observable properties just fine. But the addition of these properties itself is not an observable fact, so if you need to react to the fact that a property is added itself, then you need to use maps indeed:

const a = observable({ 
   x: 1 
})

autorun(() => {
   console.log(a.x, a.y) // won't react to a.y initially, as it is not an observable property
})

extendObservable(a, { y: 2 }) // won't trigger autorun, the creation of a new observable property is not something tracked by the autorun

a.x = 2 // this will trigger the autorun as usual
a.y = 3 // but this as well! in the last run (caused by the change in a.x) a.y was an existing observable propery of a, so mobx started tracking it

Maps don't suffer from that problem, as they have explicit support of tracking not-yet existing fields.

Note that the above could also be fixed by simply:

const a = observable({ 
   x: 1,
   y: undefined
})

@janhoeck
Copy link
Author

janhoeck commented Jun 9, 2017

Is it possible that a new added property via extendOberservable will be overserved?
Maybe a developer can define that by a new property?

extendObservable(a, {y: 2}, true)

?

Maybe that could be implemented in a new version :)
Benefits:

  • I can use the dot notation again
  • A new developer in the company doesnt get confused when he see that we are using maps at some points and at some other points we are dont using map. You understand what I mean? ;)

@urugator
Copy link
Collaborator

urugator commented Jun 9, 2017

No, it's technical limitation. MobX uses getters and setters to perform subscription/notification. MobX doesn't know if that not yet defined property was accessed in some reaction/autorun, because the MobX getter was not invoked, therefore it can't notify that reaction/autorun to re-run.
If you have an access to proxies a handler like this could do the trick (not tested):

const handler = {
  get(target, key, proxy) {
    if (!isObservable(target, key)) {
      extendObservable(target, { [key]: undefined });  
    }        
    return target[key];
  }
}
// EDIT: note it won't be enought if you're just iterating over keys
// you may need to implement "ownKeys" and "has" handler and it still may not be reliable enought

However be aware of possible performance problems, not with proxy, but with extendObservable. MobX Map and Array are optimized for frequent modifications and won't suffer from that problem.

@mweststrate
Copy link
Member

See #652

@DeDuckProject
Copy link

@mweststrate I'm wondering if there's some kind of hack to trigger a force render after extendObservable.

@mweststrate
Copy link
Member

mweststrate commented Aug 23, 2017 via email

@pstanton
Copy link

of course, re-reading the docs "after" reading this issue - it makes much more sense (i can close my stackoverflow question now). i think this page needs a little more regarding "late-add" of properties. I don't mind declaring them upfront, i think that is a win.. it's just not clearly defined in the docs (or i missed it)

@mweststrate
Copy link
Member

mweststrate commented Jan 24, 2018 via email

@mnpenner
Copy link

mnpenner commented Mar 26, 2018

MobX doesn't know if that not yet defined property was accessed in some reaction/autorun, because the MobX getter was not invoked, therefore it can't notify that reaction/autorun to re-run.

So, I understand why we can't detect direct access to a yet-to-be-defined property, but is there no way to have extendObservable trigger re-renders when JSON.stringify, Object.keys, Object.values, Object.entries, or for(... in ...) is used? i.e., extendObservable should trigger a re-render when a method that accesses all properties is invoked?


keys and values already works, can't we add a few more methods ObservableObject like toJSON to make the other methods work?

Oh...maybe not, because Object.keys doesn't actually invoke any methods on ObservableObject does it? Does it invoke [Symbol.iterator]? But even if it did, it wouldn't work in IE. That's a real bummer.

@mweststrate
Copy link
Member

toJSON might conflict with a user defined toJSON, MobX doesn't add anything automatically to observable objects (unless mobx-state-tree which has indeed toJSON implemetned on all is objects)

to make the other methods work
The other methods are built in browser methods (or even language construct), which would require built in method patches, something one shouldn't do. It sounds like an easy way out of many complex problems, but one day it will cause the next smooshgate.

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

No branches or pull requests

6 participants