最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Two immutable lists - how to make triple equality work? - Stack Overflow

programmeradmin1浏览0评论

Let's say we have an immutable object that is created using Facebook's great Immutable.js. I want to compare two lists that were produced using .map or .filter out of single source and make sure they are equal. It seems to me, that when using map/filter you are creating a new object that has nothing to do with a previous object. How can I make triple equality === work? Does it make sense at all?

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?

You can play with it here: /

Context

I am building application using React + Redux. My state has one list that contains items, that have attribute selected:

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

I want to pass only selected ids to another container, so I tried it using simple 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);

Problem is, if I set additional attributes:

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

This causes state to change and SomePlainComponent to rerender, although the list of selected Ids is exactly the same. How do I make sure pure renderer works?

Edit with some additional info

For react pure rendering I was using mixin from react-pure-render:

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

As it is not aware of props that could be immutable, they are treated as changed, i.e.

this.props = {
    ids: ImmutableList1
}

nextProps = {
    ids: ImmutableList2
}

Although both attributes ids are equal by content, they are completely different objects and do not pass ImmutableList1 === ImmutableList2 test and shouldComponentUpdate returns true. @Gavriel correctly pointed that deep equal would help, but that should be the last resort.

Anyway, I'll just apply accepted solution and problem will be solved, thanks guys! ;)

Let's say we have an immutable object that is created using Facebook's great Immutable.js. I want to compare two lists that were produced using .map or .filter out of single source and make sure they are equal. It seems to me, that when using map/filter you are creating a new object that has nothing to do with a previous object. How can I make triple equality === work? Does it make sense at all?

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?

You can play with it here: http://jsfiddle.net/eo4v1npf/1/

Context

I am building application using React + Redux. My state has one list that contains items, that have attribute selected:

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

I want to pass only selected ids to another container, so I tried it using simple 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);

Problem is, if I set additional attributes:

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

This causes state to change and SomePlainComponent to rerender, although the list of selected Ids is exactly the same. How do I make sure pure renderer works?

Edit with some additional info

For react pure rendering I was using mixin from react-pure-render:

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

As it is not aware of props that could be immutable, they are treated as changed, i.e.

this.props = {
    ids: ImmutableList1
}

nextProps = {
    ids: ImmutableList2
}

Although both attributes ids are equal by content, they are completely different objects and do not pass ImmutableList1 === ImmutableList2 test and shouldComponentUpdate returns true. @Gavriel correctly pointed that deep equal would help, but that should be the last resort.

Anyway, I'll just apply accepted solution and problem will be solved, thanks guys! ;)

Share Improve this question edited Jan 26, 2016 at 20:40 mseimys asked Jan 26, 2016 at 10:37 mseimysmseimys 6081 gold badge9 silver badges16 bronze badges 3
  • 1 Why not using deepEqual: stackoverflow.com/questions/25456013/… – Gavriel Commented Jan 26, 2016 at 10:41
  • 1 Are you sure it gets rerendered? React works the way that when one single item in a state changes the whole new state gets compared with the whole old state. Therefore the state has been looped and you might receive some logs if you log something within that lifecycle. But if nothing DOM relevant has changed then react most likely won't rerender anything. Check the shouldComponentUpdate method. – noa-dev Commented Jan 26, 2016 at 10:43
  • Because the data is immutable.. – nanobar Commented Jan 26, 2016 at 10:45
Add a comment  | 

2 Answers 2

Reset to default 18

You can never have strict equality of immutable structures since an Immutable.js object, inherently, is unique.

You can use the .is function which takes two immutable objects and compares the values within them. This works because Immutable structures implement equals and 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

If you want to keep your component pure and working with === then you can also denormalize your Redux state and store the selectedIds as a property in the store. Only update this list when an action occurs that adds/removes a selected item or toggles an item selection, but not when other arbitrary properties of the item are updated.

发布评论

评论列表(0)

  1. 暂无评论