【问题标题】:Spread Array of Objects in JavaScript在 JavaScript 中展开对象数组
【发布时间】:2020-07-03 22:20:54
【问题描述】:

我有一个react-redux 应用程序,我有一个名为dataReducer 的reducer,它的默认状态如下:

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

其中一个reducer 将元素添加到defaultState 中的data。我需要通过传递有效负载然后验证新状态来测试这个减速器。我想使用扩展运算符从旧的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 数组中可能有多个对象,但为了测试目的,我只保留一个对象让事情变得简单。

【问题讨论】:

  • 新条目进入state.data[0].elements?不是state.data?
  • newElementstate.data[0].elements 元素的结构相同,因此似乎属于它。您想将state.data 元素与不同的模型混合吗?
  • @T.J.Crowder 新条目进入elements 数组中该对象的elements,该数组与该对象具有相同的id。所以对象的idnewElement 应该匹配。

标签: javascript reactjs ecmascript-6 redux


【解决方案1】:

如果要添加到末尾,则在新状态对象中展开状态的其他方面,然后用它的当前内容覆盖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)
  ]
};

现场示例:

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])。


在评论中你问过如何处理这个问题:

假设data(存在于defaultState 中)中有多个对象条目。第一个对象的id 为1,第二个对象的id 为2。现在要添加的newElementid 为2。所以newElement 应该添加到第二个对象中。第二个对象在哪里?在第二个对象的 elements 属性内。添加不应覆盖元素数组​​中的现有条目。

您需要在data 中找到条目的索引:

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

我假设你知道它总能找到一些东西(所以它不会返回 -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
  ]
};

唯一真正的变化是在新data 的开头添加...defaultState.data.slice(0, index),并使用index 代替0

现场示例:

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;
}

【讨论】:

  • 如果我在data 数组中有多个对象,在最后添加最后一个对象条目之前,有没有办法“传播”data 中的所有现有对象条目?
  • @maverick - 是的,但我以为你想更新data 中第一个对象的elements 属性?
  • 如果newElementiddata 数组中任何对象的id 匹配,则要添加的newElement 实际上会添加到elements 数组中.
  • @maverick - 恐怕我听不懂你在说什么。你的意思是它会被添加到data 中的第一个条目中,该条目在elements 中具有匹配的id 中的任何条目?
  • @maverick - 我已添加到答案中。
猜你喜欢
  • 2015-02-25
  • 2018-02-24
  • 2015-05-23
  • 2023-01-11
  • 2016-11-24
  • 2017-10-03
  • 2023-03-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多