Search code examples
reactjsmobxmobx-react

How to run reaction callback on every possible change in MobX?


This is my simplified store:

export class ProfileStore {
  profile = {
    id: 1,
    name: "Tommy",
    todos: [
      {
        value: "Clean the room",
        done: false
      }
    ]
  };

  constructor() {
    makeObservable(this, {
      profile: observable,
    });
  }
}

My goal is to listen to every possible change inside "profile" observable. I wanted to use reaction from MobX for this but it is kinda tricky.

reaction(
      () => this.profile,
      () => {
        console.log("change");
      }
    );

Above example doesn't work at all because MobX doesn't react to values but to properties and references. So I changed it to this:

reaction(
      () => ({
        id: this.profile.id,
        name: this.profile.name,
        todos: this.profile.todos.slice()
      }),
      () => {
        console.log("change");
      }
    );

And it started working but not entirely. It listened to every change besides toggling done property in todos. And if I add another property I would need to list in here which is kinda tedious.

What's the best way to handle this problem? How to react to every possible change?

I made a codesandbox for this: link

Try to press buttons and look at the counter.


Solution

  • To react for every possible change you need to dereference every property. To do that, for example, you can use toJS method to serialize your profile object. (Or even native JSON.stringify):

    import { reaction, toJS } from 'mobx';
    
    // ...
    
        reaction(
          () => toJS(profileStore.profile),
          () => {
            setReactionCounter((state) => state + 1);
          }
        );
    

    Codesandbox