两个不可变列表——如何使三重相等起作用?

Two immutable lists - how to make triple equality work?

假设我们有一个使用 Facebook 的伟大 Immutable.js 创建的不可变对象。我想比较使用单一来源的 .map.filter 生成的两个列表,并确保它们相等。在我看来,当使用 map/filter 时,您正在创建一个与先前对象无关的新对象。如何使三重相等 === 起作用?完全有意义吗?

var list = Immutable.List([ 1, 2, 3 ]);
var list1 = list.map(function(item) { return item; })
var list2 = list.map(function(item) { return item; })

console.log("LIST1 =", list1.toJS())      // [1, 2, 3]
console.log("LIST2 =", list2.toJS())      // [1, 2, 3]
console.log("EQUAL ===?", list1===list2); // false! Why? How?

你可以在这里玩:http://jsfiddle.net/eo4v1npf/1/

上下文

我正在使用 React + Redux 构建应用程序。我的州有一个列表,其中包含具有属性 selected:

的项目
items: [
    {id: 1, selected: true},
    {id: 2, selected: false},
    {id: 3, selected: false},
    {id: 4, selected: true}
]

我只想将 选定的 ID 传递给另一个容器,所以我尝试使用简单的 connect:

function getSelectedIds(items) {
    return items
        .filter((item) => item.get("selected"))
        .map((item) =>  item.get("id"));
}

export default connect(
    (state: any) => ({
        ids: getSelectedIds(state.get("items"))
})
)(SomePlainComponent);

问题是,如果我设置额外的属性:

{id: 1, selected: true, note: "The force is strong with this one"}

这会导致状态发生变化并 SomePlainComponent 重新呈现,尽管所选 ID 的列表完全 相同。如何确保纯渲染器正常工作?

使用一些附加信息进行编辑

为了反应纯渲染,我使用了来自 react-pure-render:

的 mixin
export default function shouldPureComponentUpdate(nextProps, nextState) {
    return !shallowEqual(this.props, nextProps) ||
           !shallowEqual(this.state, nextState);
}

由于它不知道 props 可能是不可变的,因此它们被视为已更改,即

this.props = {
    ids: ImmutableList1
}

nextProps = {
    ids: ImmutableList2
}

虽然ids两个属性从内容上看是相等的,但它们是完全不同的对象,没有通过ImmutableList1 === ImmutableList2测试和shouldComponentUpdatereturnstrue。 @Gavriel 正确地指出深度平等会有所帮助,但这应该是最后的手段。

无论如何,我将应用公认的解决方案,问题将得到解决,谢谢大家! ;)

不可变结构永远不可能严格相等,因为 Immutable.js 对象本质上是唯一的。

您可以使用 .is 函数,它接受两个不可变对象并比较它们中的值。这是有效的,因为不可变结构实现 equalshashCode.

var map1 = Immutable.Map({a:1, b:1, c:1});
var map2 = Immutable.Map({a:1, b:1, c:1});
console.log(Immutable.is(map1, map2));
// true

如果你想保持你的组件纯净并与 === 一起工作,那么你也可以将你的 Redux 状态非规范化并将 selectedIds 作为 属性 存储在商店中。仅当发生 adds/removes 选定项目或切换项目选择的操作时才更新此列表,但不会更新项目的其他任意属性。