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/
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?
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! ;)
In Java 8 and earlier versions, we can use collection class utility methods like unmodifiableXXX to create immutable collection objects. If we need to create an immutable list then use the Collections. unmodifiableList() method.
The list is a data type that is mutable. Once a list has been created: Elements can be modified. Individual values can be replaced.
The common use case for the immutable methods is a collection that is initialized from known values, and that never changes. Also consider using these methods if your data changes infrequently. For optimal performance, the immutable collections store a data set that never changes.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With