Mobx - изменить наблюдаемый массив внутри реакции

Недавно я начал использовать MobX и наткнулся на концепцию reaction. Я понимаю reactions как функции, вызывающие побочные эффекты. Каковы побочные эффекты - решать мне. Я использую реакцию, чтобы обновить состояние MobX. Если вы посмотрите мой код ниже, у меня есть состояние для предметов (ингредиентов). Что я хочу сделать, так это загрузить ингредиенты из моего локального хранилища (функция getIngredients) и отобразить их в моем React. Все идет нормально. Затем, когда я обновляю ингредиент (меняю имя, цену, вес - это можно редактировать по форме), я хочу сохранить это изменение в localStorage (функция putIngredient), а затем соответствующим образом обновить свой @observable ingredients (так позже, когда я избавлюсь от localStorage и заменяю его базой данных, я отслеживаю свои изменения в MobX). Я думал, что использование MobX reaction - это неплохая идея, как с этим справиться, но когда я попытался запустить функцию updateIngredients, я получил следующую ошибку:

Encountered an uncaught exception that was thrown by a reaction or observer component, in: 'Reaction[Reaction@1]' Error: "ingredients" is read-only

Вы можете видеть, что внутри функции updateIngredients есть одна прокомментированная строка. Если я раскомментирую эту строку и закомментирую предыдущую (ingredients = ingredients.map(i => i.id === ingredient.id ? ingredient : i);), скрипт заработает. Я подумал, что могу редактировать observable переменные, просто переназначив им новые значения. Собственно, я уже так делал в функции getIngredients, где функция getIngredients возвращает новый массив. Так в чем же здесь фокус? Почему я получаю эту ошибку?

import { observable, action, reaction } from 'mobx';

import { getIngredients, putIngredient } from './localStorage';

class Ingredients {
  @observable ingredients = [];
  @observable updatedIngredient = null;

  @action.bound getIngredients(opts = {}) {
    this.ingredients = getIngredients({ ...opts });
  }

  @action.bound async putIngredient(ingredient) {
    putIngredient(ingredient);
    this.updatedIngredient = ingredient;
  }

  @action.bound updateIngredients(ingredient) {
    const { ingredients } = this;
    ingredients = ingredients.map(i => i.id === ingredient.id ? ingredient : i);
    // ingredients.replace(ingredients.map(i => i.id === ingredient.id ? ingredient : i));
  }
}

const IngredientsStore = new Ingredients();

reaction(
  () => IngredientsStore.updatedIngredient,
  (updatedIngredient) => {
    if (updatedIngredient) {
      IngredientsStore.updateIngredients(updatedIngredient);
    }
  }
)

export default IngredientsStore;

person exoslav    schedule 05.03.2020    source источник
comment
Что я понял прямо сейчас. Если я не использую { ingredients } = this в функции updateIngredients и использую this.ingredients = this.ingredients.map(...), скрипт работает должным образом.   -  person exoslav    schedule 05.03.2020


Ответы (1)


Вы слишком рано разыменовали значение.

По сути, вы создали некоторую локальную переменную ingredients, которая указывает на наблюдаемый объект, а затем переназначили этой локальной переменной другое значение. Но эта локальная переменная не наблюдаема, поэтому для MobX ничего не изменилось. В то же время this.ingredients является наблюдаемым свойством, и MobX отслеживает доступ и изменения к нему.

Подробнее здесь: https://mobx.js.org/best/react.html

person Danila    schedule 24.08.2020