mobx: Computed triggering the creation of an observable map invariant failed

I’m having issues with computeds and observable map creation.
I have a very simple test case here: https://jsfiddle.net/7e6Ltscr/

I get the following error:

VM103:59 Uncaught Error: [mobx] Invariant failed: Computed values or transformers should not invoke actions or trigger other side effects
    at mt (mobx.umd.min.js:2)
    at B (mobx.umd.min.js:2)
    at P (mobx.umd.min.js:2)
    at V (mobx.umd.min.js:2)
    at e.merge (mobx.umd.min.js:3)
    at mobx.umd.min.js:3
    at H (mobx.umd.min.js:2)
    at new e (mobx.umd.min.js:3)
    at Function.e.map (mobx.umd.min.js:2)
    at form (VM103:59)

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 22 (21 by maintainers)

Commits related to this issue

Most upvoted comments

Released as 3.1.0 😃

Ok, I think the current solution is overly complicated, both from technical perspective and to explain. I think it could simply work by changing the semantics to:

  • unobserved state can always be modified
  • observed state can be modified from inside actions, or always if not in strict mode. But: not from inside computed values

It basically means strict mode is relaxed to only protect observed state. This avoids jumping through hoops when creating and initializing new state as described in this issue and #563.

(!strict mode || in action) && (!in computed || no observers) (supports return new observables from a derivation)

Just to make things clear - please correct me if I am wrong: Returning new observables from derivations (computed) is already supported… I do this all the time (with 2.x):

@computed
get user() {
  return new ObservableUser({ cart: this.cart });
}

There are two inconveniences though - the constructor can’t modify observables (even outside of strict mode) and can’t invoke any actions (which is kind of the same thing).
As a consequence all initialization logic must be done before the observables are introduced and actions can’t be invoked as initializers from constructor or from computed itself.

So the guard is not about returning new observables, but it allows mutating unobserved state from derivation (the action si required in strict mode, but has no practical use in this case). Shouldn’t the computed allow to mutate only an unobserved state created inside the computed, similary to constructors?

However I am not sure if this should be a fix for this particular issue - imho the ObservableMap simply shoudn’t call any actions (modify state) during it’s initialization in the first place.