Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mobx: add new (array) property to observable object

I have an observable object, where I want to store some data. Keys are unique IDs for my customer entity, and values are arrays of customers' orders (which are objects themselves).

I initialize an object with:

@observable data =  {};

Then, when I get data from network, I want to store them, using:

@action
saveData(customerId, ordersForCustomer) {
  this.data = extendObservable(this.data, {
    [customerId]: observable(ordersForCustomer);
  }
}

Unfortunately, it seems my object (or its properties) are not being watched.

Why is this? And how can I fix this? Is there something special I need to know about how observable objects work with arrays as its values?

like image 202
Matúš Čongrády Avatar asked Sep 29 '16 12:09

Matúš Čongrády


People also ask

What does @observable do in MOBX?

It can be used to trap existing object properties and make them observable. Any JavaScript object (including class instances) can be passed into target . Typically makeObservable is used in the constructor of a class, and its first argument is this .

What is toJS in MOBX?

toJS. Usage: toJS(value) Recursively converts an observable object to a JavaScript object. Supports observable arrays, objects, Maps and primitives. It does NOT recurse into non-observables, these are left as they are, even if they contain observables.

What is an observable array?

ObservableArray is an array that allows listeners to track changes when they occur.


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 a map:

@observer
class App extends Component {
  @observable data = asMap({});
  constructor() {
    super();
    setInterval(() => {
      this.data.set(Math.random().toString(36).slice(-5), Math.random());
    }, 1000);
  }
  render() {
    return (
      <div> 
        { this.data.entries().map(e => <div> {e[1]} </div>) }
      </div>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('app')
);
like image 62
Tholle Avatar answered Sep 20 '22 01:09

Tholle