Eran Abir
Eran Abir

Reputation: 1097

Mobx action changing obj observable not re render React

i have a simple mobx store :


class Test {
  data;
  constructor() {
    this.data = { "1": { user: { name: "user0" } } };
    makeAutoObservable(this);
  }

  get getData() {
    return this.data;
  }

  changeData1() {
    const newObj = { user: { name: "user1" } };
    this.data["1"] = newObj;
  }

  changeData2() {
    const newObj = { user: { name: "user2" } };
    const item = this.data["1"];
    item.user.name = newObj.user.name;
  }

  changeData3() {
    const item = this.data["1"];
    const newObj = { user: { name: "user3" } };
    this.changeObjUtil(item, newObj);
  }

  changeObjUtil(obj1, obj2) {
    obj1 = obj2;
  }
}

if i will use the changeData1 and changeData2 actions it will trigger the computed and it will re render the react component but when using changeData3 action with changeObjUtil func it will not render even due object are passing as a reference to a function

any explanation ?

React Component : 
const t = new Test();

export default observer(function App() {
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <button onClick={() => t.changeData1()}>changeData1</button>
      <button onClick={() => t.changeData2()}>changeData2</button>
      <button onClick={() => t.changeData3()}>changeData2</button>

      <div>
        {Object.keys(t.getData).map((key) => {
          const item = t.getData[key];
          return <div key={key}>{item.user.name}</div>;
        })}
      </div>
    </div>
  );
});

codesandbox

thanks !

Upvotes: 0

Views: 31

Answers (1)

Danila
Danila

Reputation: 18476

changeObjUtil won't work because there is no "pass by reference" functionality available in JS/TS (see this question). It could work in another language but in JS it works differently. It's not related to MobX too.

If we expand the function a bit then this is what roughly happens:

changeObjUtil(...arguments) {
  let obj1 = arguments[0]
  let obj2 = arguments[1]

  obj1 = obj2;
}

You probably now see what's wrong.

So you just create a variable obj1 (var or let) and assign something to it (first argument in that case). Then you reassign this variable. And that's it. This variable is not connected to MobX, it's just a local variable, you only reassigned what was assigned to it before.

This question is actually pretty popular in JS interviews too see how deep you understand references etc.

For MobX you can maybe use Object.assign instead, but it will basically merge 2 objects, and if there is a field in obj1 that you don't want anymore it will be kept, e.g.:

  changeObjUtil(obj1, obj2) {
    Object.assign(obj1, obj2)
  }

Upvotes: 0

Related Questions