如何更新不可变列表以获取新列表

How to update immutable List to get a new List

我有一个不可变的 Map,如下所示

var mapA = Map({
  listA: List.of({
    id: 1,
    name: 'Name A',
    selected: false
  }, {
    id: 2,
    name: 'Name B',
    selected: false
  })
});

我想更新列表中的键 selected。根据 immutable.js 文档 list.update.

Returns a new List with an updated value at index with the return value of calling updater

但是,如果我这样做

var listB = mapA.get('listA').update(1, function (item) {
    item.selected = true;
    return item;
});

然后,检查对象是否相等,它给了我 true

console.log(listB === x.get('listA')); // true

我在这里做错了什么? immutable.js 不就是这样吗?

您也需要更新地图。

var mapB = mapA.update('listA', function(item) {
    return item.update(1, function(item) {
        item.selected = true;
        return item;
    });
});

问题是您在不可变列表中使用了 可变 值。对于列表,对象没有改变,因为 oldObject === newObject 仍然是 true.

这是一个简化的例子:

> var obj = {};
> var list = Immutable.List.of(obj);
> list2 = list.update(0, function(obj) { obj.foo = 42; return obj;});
> list2.get(0)
Object { foo: 42 }
> obj
Object { foo: 42 }
> list.get(0) === list2.get(0)
true

要解决此问题,除了还要更新地图(如果需要的话),您还必须

  • 更新时克隆对象
  • 使用地图而不是对象
  • 在这种情况下可能最好:使用 record 而不是对象

示例:

var MyRecord = new Immutable.Record({id: null, name: '', selected: false});

var mapA = Immutable.fromJS({
  listA: [
    new MyRecord({
      id: 1,
      name: 'Name A'
    }),
    new MyRecord({
      id: 2,
      name: 'Name B'
    })
  ]
});

var mapB = mapA.updateIn(['listA', 1], function(record) {
  return record.set('selected', true);
});

console.log(mapB.get('listA') === mapA.get('listA')); // false

这是我的发现。问题是 List,它基本上是普通 JavaScript 对象的集合。哪些是可变的。

解决方法很简单,我把初始对象创建改成这样

var mapA = Immutable.fromJS({
  listA: [{
    id: 1,
    name: 'Name A',
    selected: false
  }, {
    id: 2,
    name: 'Name B',
    selected: false
  }]
});

现在,以下内容按预期工作。

var mapB = mapA.setIn(['listA', 1, 'selected'], true);