Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my call to (mobx) extendObservable not triggering a re-render?

Here is the code -- pretty sure it is something about extendObservable that I just don't get, but been staring at it for quite a while now. When addSimpleProperty runs, it seems to update the object, but it doesn't trigger a render.

const {observable, action, extendObservable} = mobx;
const {observer} = mobxReact;
const {Component} = React;
class TestStore {
    @observable mySimpleObject = {};

    @action addSimpleProperty = (value) => {
        extendObservable(this.mySimpleObject, {newProp: value});
    }
}

@observer
class MyView extends Component {
constructor(props) {
    super(props);
    this.handleAddSimpleProperty = this.handleAddSimpleProperty.bind(this);
}
handleAddSimpleProperty(e) {
    this.props.myStore.addSimpleProperty("newpropertyvalue");
}

render() { 
    var simpleObjectString =JSON.stringify(this.props.myStore.mySimpleObject);
    return (<div>     
       <h3> Simple Object</h3>
       {simpleObjectString}
       <br/>
       <button onClick={this.handleAddSimpleProperty}>Add Simple Property</button>
    </div>);
    }
}

const store = new TestStore();
ReactDOM.render(<MyView myStore={store} />,    document.getElementById('mount'));
store.mySimpleObject = {prop1: "property1", prop2: "property2"};
like image 274
tristan Avatar asked Oct 30 '22 10:10

tristan


1 Answers

This problem is brought up in the Common pitfalls & best practices section of the documentation:

MobX observable objects do not detect or react to property assignments that weren't declared observable before. So MobX observable objects act as records with predefined keys. You can use extendObservable(target, props) to introduce new observable properties to an object. However object iterators like for .. in or Object.keys() won't react to this automatically. If you need a dynamically keyed object, for example to store users by id, create observable _map_s using observable.map.

So instead of using extendObservable on an observable object, you could just add a new key to an observable map.

Example

const {observable, action} = mobx;
const {observer} = mobxReact;
const {Component} = React;
class TestStore {
  mySimpleObject = observable.map({});

  @action addSimpleProperty = (value) => {
    this.mySimpleObject.set(value, {newProp: value});
  }
}

@observer
class MyView extends Component {
  constructor(props) {
    super(props);
    this.handleAddSimpleProperty = this.handleAddSimpleProperty.bind(this);
  }
  handleAddSimpleProperty(e) {
    this.props.myStore.addSimpleProperty("newpropertyvalue");
  }

  render() { 
    var simpleObjectString = this.props.myStore.mySimpleObject.values();
    return (
      <div>     
        <h3> Simple Object</h3>
        {simpleObjectString.map(e => e.newProp)}
        <br/>
        <button onClick={this.handleAddSimpleProperty}>Add Simple Property</button>
     </div>
    );
  }
}

const store = new TestStore();
ReactDOM.render(<MyView myStore={store} />, document.getElementById('mount'));
like image 124
Tholle Avatar answered Nov 15 '22 04:11

Tholle