AvantiC
AvantiC

Reputation: 377

How to update object in Mobx observable array

Codesandbox to the problem: https://codesandbox.io/s/serverless-thunder-6gj04?file=/src/store.js

I have the following store:

class PersonStore {
  persons = [];

  constructor() {
    makeAutoObservable(this, {}, { autoBind: true });
  }

  *fetchPersons() {
    const response = yield fetch(
      "https://random-data-api.com/api/users/random_user?size=5"
    );

    this.persons = yield response.json();
  }
}

export default new PersonStore();

Now, I want to update persons inside the person-list.

When I update a single field on on items inside the array like this it works as expected and the UI updates:

  update(id) {
    let updated = this.persons.find((p) => p.id === id);

    // This works...
    updated.first_name = "FOO";
  }

However, in the future I would like to pass more complex, updated data into this function. So my idea was to basically assign a totally new object with the updated values in the list.

Unfortunately, this does not work as I expected it:

  update(id) {
    let updated = this.persons.find((p) => p.id === id);

    // This does not work...
    const dummy_person = { first_name: 'foo', last_name: 'bar', id: 99 }
    updated = dummy_person
  }

My first guess was that this does not work because the objects inside the array are not "normal objects" but observables. So I created a model for the persons:

class Person {
  id = null;
  first_name = "";
  last_name = "";

  constructor() {
    makeAutoObservable(this);
    this.first_name = "FOO";
    this.last_name = "BAR";
  }
}

...but this does still not work...

  update(id) {
    let updated = this.persons.find((p) => p.id === id);

    // This does not work...
    const dummy_person = new Person()
    updated = person
  }

How can I "replace" an object inside the array here with an object containing the updated data?

Upvotes: 1

Views: 12716

Answers (1)

Danila
Danila

Reputation: 18476

As @Tholle said in the comment, you are only updating local variable, not actual object.

In simpler words this is what's happening inside your update function:

  1. let updated = this.persons.find((p) => p.id === id); - you create local variable updated and assign some object (person) to it

  2. const dummy_person = { first_name: 'foo', last_name: 'bar', id: 99 } - you create dummy object and assign it to local dummy_person constant

  3. updated = dummy_person - here you assign dummy_person value to updated variable. Basically you only reassign value of updated variable, you are not changing the person-object, but only changing to what value updated variable points.

Sometimes people explain it with boxes, like imagine that updated is a box and at first you put person inside of it and then changed you mind and put dummy_person inside, but actual person wasn't changed.

So what can you do?

As @Tholle said, you can use splice to change persons array, it will basically throw away old person and insert new one.

Or if you actually want to update old person you could use Object.assign(updated, dummy_person)

Or you could use map (although it will reassign whole array, might be unnecessary sometimes):

  update(id) {
    const dummy_person = { first_name: 'foo', last_name: 'bar', id: 99 };

    this.persons = this.persons.map(person => person.id === id ? dummy_person : person);
  }

There is a lot what you can do, depends on what you what. The main thing is understand how reactivity works!

More about this topic in the docs: https://mobx.js.org/understanding-reactivity.html

Upvotes: 4

Related Questions