Узел не может существовать дважды в дереве состояний (mobx-state-tree)

Я получаю сообщение об ошибке Error: [mobx-state-tree] A node cannot exists twice in the state tree. Failed to add SearchModel@/results/0 to path '/selectedItem' при присвоении значения selectedItem в следующей модели с помощью действия setSelectedItem. Я проверил документацию и не уверен, что вызывает эту проблему.

Ценю любую помощь. Благодарность!

const SearchModel = types
  .model({
    results: types.array(ItemModel, []),
    selectedItem:types.maybeNull(ItemModel,{ id: 0 })
  })
  .actions(self => ({   
    setSelectedItem(selItem) {
      console.log( 'typeof(selItem)', typeof(selItem));
      self.selectedItem=selItem;
    }
  }));

export default SearchModel;

person su8898    schedule 24.03.2019    source источник
comment
Вместо maybeNull попробуйте использовать вместо него reference: types.reference(ItemModel) . Одно из полей модели в ItemModel также должно быть identifier.   -  person Tholle    schedule 25.03.2019
comment
Пытаться - здесь неправильное слово - в таком случае следует использовать reference. Потому что A node cannot exist twice in the state tree.   -  person jayarjo    schedule 27.03.2019
comment
спасибо @Tholle и @jayarjo. Я не искал ссылки на исходный объект, мне просто нужна была неглубокая копия. Я использовал оператор распространения, чтобы присвоить мелкую копию selItem self.selectedItem, и проблема исчезла.   -  person su8898    schedule 27.03.2019


Ответы (5)


Для тех, кто ищет решение этой ошибки в будущем, я использовал оператор распространения, чтобы присвоить мелкую копию selItem self.selectedItem, и проблема исчезла.

Код должен был выглядеть следующим образом:

const SearchModel = types
  .model({
    results: types.array(ItemModel, []),
    selectedItem:types.maybeNull(ItemModel,{ id: 0 })
  })
  .actions(self => ({   
    setSelectedItem(selItem) {
      self.selectedItem = { ...selItem };
    }
  }));

export default SearchModel;
person su8898    schedule 27.03.2019
comment
Это не работает, если ItemModel встраивает другую модель. - person Stephen; 06.02.2020

Другое решение - использовать _.deepCopy из библиотеки Lodash. Он более универсален, чем оператор распространения, поскольку он рекурсивно спускается вниз по всему дереву, а не на один уровень. Это полезно для больших деревьев, поэтому вам не нужно увеличивать вдвое, втрое или вчетверо и иметь трудный для чтения код.

Вот как вы бы использовали его с простым магазином mobx-state-tree. Он очень элегантен, удобен в использовании.

Обратите внимание: это рекурсивная функция передачи за копированием, поэтому производительность может быть низкой, если объект слишком большой.

import _ from 'lodash';
import { types, getRoot, destroy, flow } from "mobx-state-tree";
            
const SearchModel = types
  .model({
    results: types.array(ItemModel, []),
    selectedItem:types.maybeNull(ItemModel,{ id: 0 })
  })
  .actions(self => ({   
    setSelectedItem(selItem) {
      self.selectedItem = _.deepCopy(selItem);
    }
  }));

export default SearchModel;

person Ryan Battistone    schedule 17.06.2019

Еще одна лучшая возможность: использовать функцию mobx-state-tree applySnapshot. Это должно автоматически согласовывать структуры данных, а также сохранять возможности обратного / обратного действия, чтобы вы могли воспользоваться преимуществами отмены / повтора для своего дерева состояний.

import { types, getRoot, destroy, flow, applySnapshot } from "mobx-state-tree";
            
const SearchModel = types
  .model({
    results: types.array(ItemModel, []),
    selectedItem:types.maybeNull(ItemModel,{ id: 0 })
  })
  .actions(self => ({   
    setSelectedItem(selItem) {
      applySnapshot(self.selectedItem, selItem);
    }
  }));

export default SearchModel;

person Ryan Battistone    schedule 03.12.2019

В моем случае у меня был вложенный объект «адрес» на уровне 2, поэтому мне также нужно было указать исправление для него:

setSelectedItem(selItem) {
  let item = { ...selItem };
  item.address = { ...selItem.address }
  self.selectedItem = item;
}
person gradosevic    schedule 27.05.2020

Я также столкнулся с той же проблемой в нашем React TypeScript, также наш MST store был очень вложенным и сложным, поэтому использование оператор распространения или applySnapshot не работали. Использование lodash просто решило проблему.

ШАГИ:

установить: npm i lodash.clonedeep

импорт: import cloneDeep from 'lodash/cloneDeep'

Используйте это (в моем случае): self.filteredCashGamesList.cashGameTableView = cloneDeep(self.cashGameTableView)

person wasitShafi    schedule 29.05.2021