【问题标题】:Updating nested immutable state (redux)更新嵌套的不可变状态 (redux)
【发布时间】:2018-10-25 14:01:17
【问题描述】:

我正在寻找将新项目对象添加到减速器中的类别。 reducer 接收一个类别索引和一个新的 item 对象。

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

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 上还有许多其他类似的问题,但没有一个具有相同的结构。

更新

reducer 的其余部分如下所示:

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 工作正常,但它正在改变状态

【问题讨论】:

标签: javascript redux react-redux


【解决方案1】:

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

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

【讨论】:

  • 非常好,正是我想要的:)
【解决方案2】:

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

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

要避免这种情况,您必须创建一个 数组的副本。您可以像这样在普通的 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 的副本,newDataObject 被推送到 newState 数组的最后一个索引。

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

    return {
        ...state,
        categories
    }

它应该返回一个新的对象,并将类别推到它上面。你可以使用 es6 的Object.assign() 来合并两个对象,它会返回一个包含两者的全新对象。

将您的退货声明更改为:

        return Object.assign({}, state, categories)

【讨论】:

  • 'push' 仍然是一个可以用来将新项目合并到数组中的函数吗?我在任何地方都读过关于不变性的内容,这令人不悦。这与扩展运算符的工作方式有何不同?
  • Push 将改变你推送到的数组(不是不可变的,并且会破坏你的减速器)。你应该避免在你的 reducer 中使用 push,除非你推送的数组是一个新数组。扩展运算符不会改变您的原始数据,而是在新数组中返回原始数据的副本。
  • 请注意,展开运算符只会深入一层。 Spread 不适合使用扩展语法复制多维数组。您将不得不递归地分布在每个嵌套对象上(这不是一个简单的解决方案,但那里有实现)。如果你需要这个,我建议你看看 ImmutableJS 的实现。
猜你喜欢
  • 2021-04-15
  • 1970-01-01
  • 1970-01-01
  • 2017-04-30
  • 2019-01-10
  • 2019-10-03
  • 2017-08-08
  • 2020-02-08
  • 1970-01-01
相关资源
最近更新 更多