mobx: Don't autoconvert function without arguments into getters

For me, one of the least pleasant parts of mobx is automatic converting function without arguments to getters.

The problem

// ordinar mobx store
class KittensStore {
  @observable myKittens = [];
  addKitten(kitten) {
    this.myKittens.push(kitten);
  }
}

// somewhere in ui:
kittensStore.addKitten({
  name: "Kitty",
  sayMeow: asReference(() => alert('meow')), // ui have to know about store internals
                                             // and that the values will be mobxified
  sayText: (text) => alert(text), // doesn't need asReference, feels inconcistent
  onClick: () => alert('hello'), // damn, I've forgotten about autoconverting
});

Proposition

It would be really nice not to make such a bold assumption. We can introduce special modifier computed, so if one really want to create getter, he can show it explicitly:

extendObservable(this, {
  count: 0,
  doubleCount: computed(() => this.count * 2),
});

Pros:

  • No magic
  • More consistent API
  • Any data can be accessed the same way after mobxification
  • Less surprises for the new developers
  • Easy to find and rewrite ‘asGetter’ accordingly (for example, during upgrading to es2015)

Cons:

  • Breaking change

PS: I know about asFlat

UPD changed asGetter -> computed

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 23 (15 by maintainers)

Commits related to this issue

Most upvoted comments

it is wrapped in an action indeed. thanks for the idea btw, despite my initial scepticism I really like it! wish i had realized half a year ago this is valid es5 ☺ I think I’m gonna push this as the idiomatic way to create computeds without decorator

Op wo 31 aug. 2016 22:52 schreef urugator notifications@github.com:

You have a typo in test description “… plan object …”. Btw is setter wrapped into an action or left as is?

— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub https://github.com/mobxjs/mobx/issues/421#issuecomment-243897677, or mute the thread https://github.com/notifications/unsubscribe-auth/ABvGhOHAtYwIrwU4rf5cDVMbqeRZURodks5qlemWgaJpZM4JP03A .

@mweststrate Actual prop descriptor, obtained by Object.getOwnPropertyDescriptor()

extendObservable(this, {
   side: 1,
   get volume() { return this.side * this.side; }, // turns into computed prop
   viewClass: function() {....} // plain functions are simply observed by reference, arg count doesn't matter
})

Btw I actually never realized this is valid ES5 syntax 😕

Note that the above syntax won’t work afaik, should be get volume() { ... } which is slightly uglier.

But yes, I admit it looks tempting. Was quite easy to setup, see above commit 😃

As @andykog made clear, there is no point in observing a getter field, because it’s “value” can’t be changed in an observable way. So a getter defined in an observable is practically unobservable (or am I missing something?), which seems a bit wierd to me.

Also, I think, it kinda makes sense to turn getter into computed, because getter is in fact a normal field, which value is a just a derivation of other field/s. If those other fields form an observable state of the object, shouldn’t their derivations form an observable state as well? (And note, that we are explicitely marking the getter as observable, by placing it in observable object definition, so one can still create a normal getter)

Similary to: “What if somebody actually wants to create a normal getter”? I could ask: What if somebody actually wants to create a normal function? But that’s not the problem we are dealing with. We don’t want the function to be normal, but observable. The actual question is, observable how? As a reference or something else (computed)? So I think, we should analogically ask in which way the getter should be observable.

And the least importantly, it’s more appealing 😃

extendObservable(this, {
  side: 1, 
  get volume() { return this.side * this.side }
  set volume(v) { this.side = Math.sqrt(v) }
})

EDIT:

somehow it feels a bit arbitrary that get means computed

In that case I think the following should feel arbitrary as well: {} means observable({}) [] means observable([])

EDIT: fixed getter syntax

@urugator sorry you’re right. On the last question, what do you mean by getters? Anything that looks like a prop descriptor, or actual property descriptors? That might be convenient for ES6 user indeed

@andykog Thought about it, I think the current behavior should indeed be deprecated, as in unexpectly ‘breaks’ the type of an assignment, especially in less obvious cases like:

const x = observable({ y: null })

x.y = { z: function() { console.log("hi") }

x.y.z() // nope