两个不可变列表 - 如何使三重相等起作用?
Posted
技术标签:
【中文标题】两个不可变列表 - 如何使三重相等起作用?【英文标题】:Two immutable lists - how to make triple equality work? 【发布时间】:2016-05-02 21:35:50 【问题描述】:假设我们有一个使用 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
]
我只想将 选定的 ids 传递给另一个容器,所以我尝试使用简单的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);
由于它不知道可能是不可变的道具,因此它们被视为已更改,即
this.props =
ids: ImmutableList1
nextProps =
ids: ImmutableList2
虽然ids
两个属性在内容上是相等的,但它们是完全不同的对象,没有通过ImmutableList1 === ImmutableList2
测试,shouldComponentUpdate
返回true
。 @Gavriel 正确地指出深度平等会有所帮助,但这应该是最后的手段。
无论如何,我会应用接受的解决方案,问题就会得到解决,谢谢大家! ;)
【问题讨论】:
为什么不使用 deepEqual:***.com/questions/25456013/… 你确定它会重新渲染吗? React 的工作方式是,当状态中的一个项目发生变化时,整个新状态与整个旧状态进行比较。因此,状态已经循环,如果您在该生命周期内记录某些内容,您可能会收到一些日志。但是,如果 DOM 相关的内容没有发生任何变化,那么 react 很可能不会重新渲染任何内容。检查 shouldComponentUpdate 方法。 因为数据是不可变的.. 【参考方案1】:由于 Immutable.js 对象本质上是唯一的,因此您永远不能严格相等的不可变结构。
您可以使用.is
函数,该函数接受两个不可变对象并比较其中的值。这是因为不可变结构实现了equals
和hashCode
。
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
【讨论】:
【参考方案2】:如果您想保持组件纯净并使用===
,那么您还可以对 Redux 状态进行非规范化并将 selectedIds 作为属性存储在存储中。仅当发生添加/删除所选项目或切换项目选择的操作时更新此列表,而不是在更新项目的其他任意属性时更新此列表。
【讨论】:
这听起来是个好主意,但是我的状态将存储在两个地方,我必须使用...操作来同步它?状态看起来像: selectedItems: [1], items: [ id: 1, selected: true, id: 2, selected: false ]
在处理选择操作时,我需要更新两个状态属性,这听起来很容易,但我必须一直这样做:当我删除一个项目,当我切换到下一页等。这会导致代码周围有很多额外的逻辑:)
确实,如果操作导致选择发生变化,那么当前修改items
属性的每个reducer 也需要修改selectedItems
属性。大概您只有少数几个受到影响的操作?第二种选择是从项目中删除选定的属性,并仅将选定的项目作为单独的属性存储在商店中,作为列表或作为集合:@987654325 @ 或 selectedIdSet: Set(1,...), items:id:1,name:"foo",...]
以上是关于两个不可变列表 - 如何使三重相等起作用?的主要内容,如果未能解决你的问题,请参考以下文章