Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MobX: how to change an observable value in response to another value's change?

Say I have an object with two MobX @observable fields:

class Address {
  @observable country
  @observable city
}

When one of them changes, I want to call a function that changes the other one. For example, when country changes, I might want to clear city if its value isn't valid for the new country.

Is there a good pattern for this?

I don't think I can use autorun. Since I'm trying to change an observable value, and I have enforceActions turned on, I need to change it in an action. But this throws an error "Autorun does not accept actions since actions are untrackable":

autorun(action(() => {
    if (this.country === 'US' && this.city === 'Paris') this.city = '';
}));

I know I can add a @computed function that returns either city or a new value. But then the original value of city is still there, and will get returned when country changes back. I don't want this; I want to permanently change city.

@computed get realCity() {
    if (this.country === 'US' && this.city === 'Paris') return '';
    return this.city;
}
like image 853
JW. Avatar asked Mar 06 '23 01:03

JW.


2 Answers

You could use observe to observe the object in the constructor, and reset the city if the country changed.

Example (JSBin)

class Address {
  @observable country = 'Sweden';
  @observable city = 'Stockholm';

  constructor() {
    observe(this, (change) => {
      if (change.name === 'country') {
        // Put your logic for changing the city here
        this.city = '';
      }
    });
  }
}

const address = new Address();

console.log(`${address.country}, ${address.city}`);
address.country = 'Spain';
console.log(`${address.country}, ${address.city}`);
like image 52
Tholle Avatar answered Mar 17 '23 14:03

Tholle


I think you should look at your problem with a different angle.

The thing I am asking myself is: isn't there something you could do to avoid the problem you face altogether?

Why allow the situation to happen in the first place?

  • If a country is set: create a subset of cities for that country.
  • If a city is set and the country changes, unset the city at the same time.

Regarding mobx specific patterns this bit of docs is useful:

As a rule of thumb: use autorun if you have a function that should run automatically but that doesn't result in a new value. Use computed for everything else. Autoruns are about initiating effects, not about producing new values. Mobx docs

like image 32
Kev Avatar answered Mar 17 '23 12:03

Kev