更新嵌套的不可变状态(redux)

时间:2018-10-25 14:01:18

标签: javascript redux react-redux

我希望将新的item对象添加到reducer的类别中。 reducer接收一个类别索引和一个新的项目对象。

有人知道用这种数据结构不变地更新状态的最佳方法:

const initialState = {    
    categories: [
        {
            id: 1,
            name: "vegetables",
            items: [
                {name: "potatoes", id: Math.floor(Math.random() * 99999)},
                {name: "carrots", id: Math.floor(Math.random() * 99999)}
            ] 
        },
        {
            id: 2,
            name: "dairy",
            items: [
                {name: "milk", id: Math.floor(Math.random() * 99999)},
                {name: "cheese", id: Math.floor(Math.random() * 99999)}
            ] 
        },
        {
            id: 3,
            name: "meat",
            items: [
                {name: "chicken", id: Math.floor(Math.random() * 99999)}
            ] 
        }
    ]
}

还是最好使用外部软件包,例如immutable.js?

关于stackoverflow还有很多其他类似的问题,但是没有一个具有相同的结构。

更新

减速器的其余部分看起来像:

const shoppingItemsReducer = (state = initialState, action) => {

    switch (action.type) {
        case ADD_SHOPPING_ITEM:

        const categories = [...state.categories];


     categories[action.selectedCategoryIndex].items.push(action.newItemObj);

            return {
                ...state,
                categories
            }
        default:
            return state
    }

}

使用push可以正常工作,但它可以改变状态

2 个答案:

答案 0 :(得分:1)

您可以在不使用推送的情况下执行以下操作

const initialState = {    
    categories: [
        {
            id: 1,
            name: "vegetables",
            items: [
                {name: "potatoes", id: Math.floor(Math.random() * 99999)},
                {name: "carrots", id: Math.floor(Math.random() * 99999)}
            ] 
        },
        {
            id: 2,
            name: "dairy",
            items: [
                {name: "milk", id: Math.floor(Math.random() * 99999)},
                {name: "cheese", id: Math.floor(Math.random() * 99999)}
            ] 
        },
        {
            id: 3,
            name: "meat",
            items: [
                {name: "chicken", id: Math.floor(Math.random() * 99999)}
            ] 
        }
    ]
}

const categoryId = 2; // categoy want to update
cosnt newItem = {name: "Butter", id: Math.floor(Math.random() * 99999)}

const newState = {
  ...initialState, // or state
  categories: initialState.categories.map(category => {
    if(category.id === categoryId) {
      return {
        ...category,
        items: [
          ...category.items,
          newItem
        ]
      }
    }
    return category;
  )
}

答案 1 :(得分:0)

包含基本类型的变量将始终指向实际值。因此,如果将其传递给另一个变量,则另一个变量将获得该值的新副本。

但是,对象和数组始终通过引用传递。因此,如果要将对象或数组传递给另一个变量,则它们都将引用同一原始对象。如果要修改引用原始变量的任何变量,它也会修改原始对象/数组。

要避免这种情况,您必须创建Array的 new 副本。您可以像这样使用普通的javascript:

const initialState = [
    {
        id: 1,
        name: "category 1",
        items: [
            {name: "item 1", id: 1},
            {name: "item 2", id: 2}
        ] 
    },
    {
        id: 2,
        name: "category 2",
        items: [
            {name: "item 3", id: 3},
            {name: "item 4", id: 4}
        ] 
    },
    {
        id: 3,
        name: "category 3",
        items: [
            {name: "item 5", id: 5},
            {name: "item 6", id: 6}
        ] 
    }
]


const newState = [...initialState, newDataObject]

newState是一个新创建的数组,其中包含initialState的{​​{1}}副本被推到newDataObject数组的最后一个索引。

编辑:我看到您使用redux reducer更新了您的问题。您当前正在返回一个引用初始状态的对象:

newState

它应该返回一个新对象,并将其推入目录。您可以使用es6的 return { ...state, categories } 合并两个对象,它将返回一个包含两个对象的全新对象。

将您的退货单更改为:

Object.assign()