Распределить массив объектов в JavaScript - PullRequest
2 голосов
/ 23 марта 2020

У меня есть приложение react-redux, и у меня есть редуктор с именем dataReducer, состояние которого по умолчанию выглядит следующим образом:

const defaultState = {
  isLoading: false,
  data: [{
    id: 1,
    label: 'abc',
    elements: [{ color: 'red', id: 1}],
  }],
};

Один из редукторов добавляет элементы к data в defaultState. Мне нужно проверить этот редуктор, передав полезную нагрузку и затем проверив новое состояние. Я хочу использовать оператор распространения для создания нового состояния из старого defaultState, но у меня возникли некоторые проблемы с его достижением. Я попробовал следующее, но это не работает:

const newElement = {
  colour: 'blue',
  id: 1
};

const newState = [
  {
    ...defaultState.data[0],
    elements: [{
      ...defaultState.data[0].elements,
      ...newElement,
    }]
  }
];

expect(dataReducer(defaultState, action)).toEqual(newState); // returns false

Было бы здорово, если бы я мог как-то избежать использования индекса массива (defaultState.data[0]), поскольку в массиве defaultState в массиве может быть несколько объектов реальное приложение, хотя для целей тестирования я держу только один объект, чтобы упростить задачу.

1 Ответ

2 голосов
/ 23 марта 2020

Если вы добавляете в конец, вы распределяете другие аспекты состояния в новом объекте состояния, затем переопределяете data текущим содержимым этого, а затем новую запись:

const newState = {               // New state is an object, not an aray
  ...defaultState,               // Get everything from defaultState
  data: [                        // Replace `data` array with a new array
    {
      ...defaultState.data[0],   // with the first item's contents
      elements: [                // updating its `elements` array
        ...defaultState.data[0].elements,
        newElement
      ]
    },
    ...defaultState.data.slice(1) // include any after the first (none in your example)
  ]
};

Live Пример:

const defaultState = {
  isLoading: false,
  data: [{
    id: 1,
    label: 'abc',
    elements: [{ color: 'red', id: 1}],
  }],
};

const newElement = {
  colour: 'blue',
  id: 1
};

const newState = {               // New state is an object, not an aray
  ...defaultState,               // Get everything from defaultState
  data: [                        // Replace `data` array with a new array
    {
      ...defaultState.data[0],   // with the first item's contents
      elements: [                // updating its `elements` array
        ...defaultState.data[0].elements,
        newElement
      ]
    },
    ...defaultState.data.slice(1) // include any after the first (none in your example)
  ]
};

console.log(newState);
.as-console-wrapper {
    max-height: 100% !important;
}

Нет смысла указывать нужную запись в data (например, data[0]).


In комментарий, который вы спросили, как с этим справиться:

Допустим, data (присутствует внутри defaultState) содержит несколько записей объектов. Первый объект имеет id из 1, второй имеет id из 2. Теперь добавляемый newElement имеет id из 2. Таким образом, newElement должен быть добавлен ко второму объекту. Где во втором объекте? Внутри элемента свойство второго объекта. Добавление не должно перезаписывать существующие записи в массиве элементов.

Вам нужно будет найти индекс записи в data:

const index = defaultState.data.findIndex(({id}) => id === newElement.id);

I ' Я собираюсь предположить, что вы знаете, что всегда найдете что-то (поэтому оно не вернет -1). Чтобы затем применить это index к приведенному выше коду, вы должны сделать следующее:

const newState = {                        // New state is an object, not an aray
  ...defaultState,                        // Get everything from defaultState
  data: [                                 // Replace `data` array with a new array
    ...defaultState.data.slice(0, index), // Include all entries prior to the one we're modifying
    {
      ...defaultState.data[index],        // Include the entry we're modifying...
      elements: [                         // ...updating its `elements` array
        ...defaultState.data[index].elements,
        newElement
      ]
    },
    ...defaultState.data.slice(index + 1) // include any after the one we're updating
  ]
};

Единственное реальное изменение - это добавление ...defaultState.data.slice(0, index) в начале нового data и использование index вместо 0.

Live Пример:

const defaultState = {
  isLoading: false,
  data: [
      {
        id: 1,
        label: 'abc',
        elements: [{ color: 'red', id: 1}],
      },
      {
        id: 2,
        label: 'def',
        elements: [{ color: 'green', id: 2}],
      },
      {
        id: 3,
        label: 'ghi',
        elements: [{ color: 'yellow', id: 3}],
      }
  ],
};

const newElement = {
  colour: 'blue',
  id: 2
};

const index = defaultState.data.findIndex(({id}) => id === newElement.id);

const newState = {                        // New state is an object, not an aray
  ...defaultState,                        // Get everything from defaultState
  data: [                                 // Replace `data` array with a new array
    ...defaultState.data.slice(0, index), // Include all entries prior to the one we're modifying
    {
      ...defaultState.data[index],        // Include the entry we're modifying...
      elements: [                         // ...updating its `elements` array
        ...defaultState.data[index].elements,
        newElement
      ]
    },
    ...defaultState.data.slice(index + 1) // include any after the one we're updating
  ]
};

console.log(newState);
.as-console-wrapper {
    max-height: 100% !important;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...