Mobx: Shared elements when using Store classes with @observable

Created on 14 Apr 2016  路  4Comments  路  Source: mobxjs/mobx

I seem to be encountering some weird behavior when working with observables in classes (or there's a crucial detail about property initializers that I'm failing to grasp).

Specifically, certain properties are being shared across all instances of a class (while others remain localized), so that every time a new instance is created, all the other instances update to reflect the new value.

For example:

class Field {
  @observable fieldName;
  @observable bound = {
    field: null,
    lowerBound: 0,
    upperBound: null,
  }

  constructor(fieldName) {
    this.fieldName = fieldName;
    // All instances of `Field` will have the same bound.field for some reason
    this.bound.field = fieldName;
  }
}

In this case, there is a component that uses fieldName in the header, and bound in the body of a panel. Every time you add a new Field to the store, the new component renders with the correct fieldName in the header, but not in the body. For some reason, this.bound.field is shared across all instances, but this.fieldName is not (notice how the header reflects individual field names, but all 3 components now say "field 3" in the body, since field 3 was the most recent value.)

image

On the other hand, this works:

class Field {
  @observable fieldName;

  constructor(fieldName) {
    this.fieldName = fieldName;
    // In this case, each instance will have its own unique `bound` property
    extendObservable(this, {
      bound: {
        field: fieldName,
        lowerBound: 0,
        upperBound: null,
      },
    });
}

Using extendObservable to add the entire property to the instance in the constructor does successfully localize the entire bound property, so that adding a new Field to the store correctly adds a fully encapsulated instance.

image

An especially weird side note is that even though this.bound.field is shared across all instances, the other two properties of this.bound are not. This means that I can change the other fields of bound and those changes are reflected solely in the component they belong to, even though the field property is still being shared across all three.

image

For reference, I'm using Babel 6 with the following plugins:

  • "babel-plugin-array-includes": "^2.0.3",
  • "babel-plugin-transform-decorators-legacy": "^1.3.4",
  • "babel-plugin-transform-object-assign": "^6.5.0",
  • "babel-preset-es2015": "^6.6.0",
  • "babel-preset-react": "^6.5.0",
  • "babel-preset-react-hmre": "^1.1.1",
  • "babel-preset-stage-1": "^6.5.0",

In addition, I'm using these packages:

  • "mobx": "^2.0.4",
  • "mobx-react": "^3.0.3",
  • "react": "^0.14.8",
  • "react-dom": "^0.14.8"
馃悰 bug

Most helpful comment

Made #192 to fix this

All 4 comments

Made #192 to fix this

@andykog nice!

Released fix as 2.1.3. Thanks for the extensive report @cafreeman, thanks for fixing @andykog :)

@mweststrate @andykog I just did this in a project using mobx 2.4.3 at the moment, and it still breaks. Any idea how it can be fixed?

Was this page helpful?
0 / 5 - 0 ratings