Deep changes of objects cannot be observed in mobx

Viewed 728

I am managing the state using mobx(6.3.12) in react. But I can't observe deep changes of objects.

Here my sources.

  1. store
    class ApiStore {
        values = {
            name: '',
            author: '',
        }
        constructor() {
            makeObservable(this, {
                values: observable,
                setValues: action
            })
        }
              
        setValues(key, value) {
            values[key] = value;
        }
    }
  1. observe(not all work)

    store = new ApiStore();
    
    useEffect(() => {
      console.log(values);
    }, [store.values]);
    
    
    
    store = new ApiStore();
        useEffect(() => {
            autorun(() => console.log(store.values));
        }, [])
    
  2. state change

     ...
     <NormalTextValue
        onChange={(e) => store.setValues(id, e.target.value)}
        value={store.values[id]}/>
    
    
    

I realized that not being able to read deep change was the cause. Changes can be detected by accessing them via dereferencing. (ex. [store.values.name])

So I changed it to the following to get the desired result.

   setValues(key, value) {
       this.values[key] = value;
       this.values = {...this.values};
    }

Q: According to the docs, the default option of observable is known as .deep. So I thought I could automatically detect deep change of object (through this.values[key] = values). But it seems that the reference value of the object has to be changed. Could you please explain this or point out the error?

1 Answers

deep means that the object will be deeply observable, so every inner property or inner object/array, basically everything will be made observable too.

To actually react to the change of some observable value you need to use it, or dot into it or touch it, whatever you call it. So if you want to react to the name changes then you need to somehow use store.values.name inside of your component. Same with other fields. MobX will subscribe your component for changes only if you are actually explicitly using those values. Otherwise why would you want to rerender component if it does not use anything? It will be just an extra rerender.

In your case you are only using store.values, basically only the object itself, not its fields. That's why everything rerenders when you reassign the whole object with destructuring. You are using the object -> object changes -> component rerenders.

If you are not using all the fields but still want to somehow react to them you can, for example, use JSON.stringify or MobX method toJS, like that:

    useEffect(() => {
        autorun(() => console.log(JSON.stringify(store.values)));
    }, [])

Related