6

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?

ruffin
  • 16,507
  • 9
  • 88
  • 138
Matúš Čongrády
  • 1,340
  • 3
  • 17
  • 29
  • It will be mutated so you don't need `this.data = ...`. Try just `extendObservable(this.data, { [customerId]: ordersForCustomer }`. Does that work? – Tholle Sep 29 '16 at 13:59
  • 1
    No, it doesn't. I solved my problem by using observable map. But still, I would like to know what was the case. – Matúš Čongrády Sep 29 '16 at 14:25

1 Answers1

8

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')
);
Tholle
  • 108,070
  • 19
  • 198
  • 189
  • 1
    As of today, at least, it looks like the text about using maps has moved to [the pitfalls page](https://mobx.js.org/best/pitfalls.html). There's also a decent discussion about maps in a video by Weststrate here: [*Use observable objects, arrays, and maps to store state in MobX*](https://egghead.io/lessons/react-mobx-fundamentals-using-observable-objects-arrays-and-maps-to-store-state), with maps-specific info starting around 2:28. Still wish you could just wrap `{}` like you can `[]` and have hashes shimmed (no new OM) like observable arrays. No dice, apparently. – ruffin Feb 27 '17 at 16:01