Mobx: @computed expression not always memoized

Created on 22 Dec 2016  ·  2Comments  ·  Source: mobxjs/mobx

First off, MobX is freaking amazing! Am writing a big spreadsheet component with it... code is scaling well, easy to extend. Would be insane without MobX...

I'm chasing some re-render issues and find some odd behavior. Using mobx 2.7.0, testing with Karma inside Chrome 55.

Issue: A @computed expression is being re-evaluated, although the underlying data is unchanged.

class ObservablePerson {
  @observable first;
  @observable last;
  fullNameComputations = 0;

  constructor(first, last) {
    this.first = first;
    this.last = last;
  }

  @computed get fullName() {
    this.fullNameComputations++;
    console.log("Computing full name!");
    return `${this.first} ${this.last}`;
  }

  @computed get summary() {
    return this.fullName;
  }
}

it("calcs fullname when needed", () => {
  useStrict(false);
  var hjs = new ObservablePerson("homer", "simpson");
  //autorun(()=> {
  //  console.log(jw.fullName);
  //});

  //console.log(hjs.summary);

  expect(hjs.fullName).to.equal("homer simpson");
  expect(hjs.fullNameComputations).to.equal(1);

  expect(hjs.fullName).to.equal("homer simpson");
  expect(hjs.fullName).to.equal("homer simpson");
  expect(hjs.fullName).to.equal("homer simpson");
  expect(hjs.fullName).to.equal("homer simpson");
  expect(hjs.fullName).to.equal("homer simpson");

  expect(hjs.fullNameComputations).to.equal(1);
});

fullName is not memoized, and the test fails:

16:56:06 test.1 | LOG: 'Computing full name!'
16:56:06 test.1 | LOG: 'Computing full name!'
16:56:06 test.1 | LOG: 'Computing full name!'
16:56:06 test.1 | LOG: 'Computing full name!'
16:56:06 test.1 | LOG: 'Computing full name!'
16:56:06 test.1 | LOG: 'Computing full name!'
16:56:06 test.1 | Chrome 55.0.2883 (Mac OS X 10.10.4) ValidatedGridState computed expressions calcs fullname when needed FAILED
16:56:06 test.1 |   AssertionError: expected 6 to equal 1

Interestingly, the behavior can be changed by uncommenting the 2 blocks. Autorun forces memoization and makes the test pass.

Uncommenting console.log(hjs.summary); results in 2 recalcs.

Is this expected?

Most helpful comment

It's expected. If it's not observed it will not be memoized.

Per: https://mobxjs.github.io/mobx/best/pitfalls.html
Computed values run more often then expected

If a computed property is not in use by some reaction (autorun,
observer etc),
computed expressions will be evaluated lazily; each time their value is
requested (so they just act as normal property). Computed values will only
track their dependencies if they are observed. This allows MobX to
automatically suspend computations that are not actively in use. See this
blog
https://medium.com/@mweststrate/becoming-fully-reactive-an-in-depth-explanation-of-mobservable-55995262a254
or issue #356 https://github.com/mobxjs/mobx/issues/356 for an
explanation. So if you fiddle arounds, computed properties might not seem
efficient. But when applied in a project that uses observer, autorun etc,
they become very efficient.

On Thu, Dec 22, 2016 at 4:08 PM, Jeff Winkler notifications@github.com
wrote:

I'm chasing some re-render issues and find some odd behavior. Using mobx
2.7.0, testing with Karma inside Chrome 55.

Issue: A @computed https://github.com/computed expression is being
re-evaluated, although the underlying data is unchanged.

class ObservablePerson {
@observable first;
@observable last;
fullNameComputations = 0;

constructor(first, last) {
this.first = first;
this.last = last;
}

@computed get fullName() {
this.fullNameComputations++;
console.log("Computing full name!");
return ${this.first} ${this.last};
}

@computed get summary() {
return this.fullName;
}
}

it("calcs fullname when needed", () => {
useStrict(false);
var hjs = new ObservablePerson("homer", "simpson");
//autorun(()=> {
// console.log(jw.fullName);
//});

//console.log(hjs.summary);

expect(hjs.fullName).to.equal("homer simpson");
expect(hjs.fullNameComputations).to.equal(1);

expect(hjs.fullName).to.equal("homer simpson");
expect(hjs.fullName).to.equal("homer simpson");
expect(hjs.fullName).to.equal("homer simpson");
expect(hjs.fullName).to.equal("homer simpson");
expect(hjs.fullName).to.equal("homer simpson");

expect(hjs.fullNameComputations).to.equal(1);
});

fullName is not memoized, and the test fails:

16:56:06 test.1 | LOG: 'Computing full name!'
16:56:06 test.1 | LOG: 'Computing full name!'
16:56:06 test.1 | LOG: 'Computing full name!'
16:56:06 test.1 | LOG: 'Computing full name!'
16:56:06 test.1 | LOG: 'Computing full name!'
16:56:06 test.1 | LOG: 'Computing full name!'
16:56:06 test.1 | Chrome 55.0.2883 (Mac OS X 10.10.4) ValidatedGridState computed expressions calcs fullname when needed FAILED
16:56:06 test.1 | AssertionError: expected 6 to equal 1

Interestingly, the behavior can be changed by uncommenting the 2
blocks. Autorun forces memoization and makes the test pass.

Uncommenting console.log(hjs.summary); results in 2 recalcs.

Is this expected?


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/718, or mute the thread
https://github.com/notifications/unsubscribe-auth/AAIrcnT7uhNjNJc4VHrSigcvcJDlQeRTks5rKvTZgaJpZM4LUayr
.

--
-Matt Ruby-
[email protected]

All 2 comments

It's expected. If it's not observed it will not be memoized.

Per: https://mobxjs.github.io/mobx/best/pitfalls.html
Computed values run more often then expected

If a computed property is not in use by some reaction (autorun,
observer etc),
computed expressions will be evaluated lazily; each time their value is
requested (so they just act as normal property). Computed values will only
track their dependencies if they are observed. This allows MobX to
automatically suspend computations that are not actively in use. See this
blog
https://medium.com/@mweststrate/becoming-fully-reactive-an-in-depth-explanation-of-mobservable-55995262a254
or issue #356 https://github.com/mobxjs/mobx/issues/356 for an
explanation. So if you fiddle arounds, computed properties might not seem
efficient. But when applied in a project that uses observer, autorun etc,
they become very efficient.

On Thu, Dec 22, 2016 at 4:08 PM, Jeff Winkler notifications@github.com
wrote:

I'm chasing some re-render issues and find some odd behavior. Using mobx
2.7.0, testing with Karma inside Chrome 55.

Issue: A @computed https://github.com/computed expression is being
re-evaluated, although the underlying data is unchanged.

class ObservablePerson {
@observable first;
@observable last;
fullNameComputations = 0;

constructor(first, last) {
this.first = first;
this.last = last;
}

@computed get fullName() {
this.fullNameComputations++;
console.log("Computing full name!");
return ${this.first} ${this.last};
}

@computed get summary() {
return this.fullName;
}
}

it("calcs fullname when needed", () => {
useStrict(false);
var hjs = new ObservablePerson("homer", "simpson");
//autorun(()=> {
// console.log(jw.fullName);
//});

//console.log(hjs.summary);

expect(hjs.fullName).to.equal("homer simpson");
expect(hjs.fullNameComputations).to.equal(1);

expect(hjs.fullName).to.equal("homer simpson");
expect(hjs.fullName).to.equal("homer simpson");
expect(hjs.fullName).to.equal("homer simpson");
expect(hjs.fullName).to.equal("homer simpson");
expect(hjs.fullName).to.equal("homer simpson");

expect(hjs.fullNameComputations).to.equal(1);
});

fullName is not memoized, and the test fails:

16:56:06 test.1 | LOG: 'Computing full name!'
16:56:06 test.1 | LOG: 'Computing full name!'
16:56:06 test.1 | LOG: 'Computing full name!'
16:56:06 test.1 | LOG: 'Computing full name!'
16:56:06 test.1 | LOG: 'Computing full name!'
16:56:06 test.1 | LOG: 'Computing full name!'
16:56:06 test.1 | Chrome 55.0.2883 (Mac OS X 10.10.4) ValidatedGridState computed expressions calcs fullname when needed FAILED
16:56:06 test.1 | AssertionError: expected 6 to equal 1

Interestingly, the behavior can be changed by uncommenting the 2
blocks. Autorun forces memoization and makes the test pass.

Uncommenting console.log(hjs.summary); results in 2 recalcs.

Is this expected?


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/718, or mute the thread
https://github.com/notifications/unsubscribe-auth/AAIrcnT7uhNjNJc4VHrSigcvcJDlQeRTks5rKvTZgaJpZM4LUayr
.

--
-Matt Ruby-
[email protected]

👍 thanks for the thorough explanation Matt!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

AriaFallah picture AriaFallah  ·  63Comments

winterbe picture winterbe  ·  34Comments

arackaf picture arackaf  ·  29Comments

Keats picture Keats  ·  45Comments

FouMez picture FouMez  ·  31Comments