Reputation: 15488
I'm new to immutablejs and have managed to update a property of an object stored in an array of objects. My goal is to simplify my development but I feel like I'v made it more complicated. Clearly, I'm missing something to make this simpler.
I created to stackblitz projects, one with the code without immutablejs https://stackblitz.com/edit/react-before-immutable , and one with immutablejs https://stackblitz.com/edit/react-after-immutable
(code below also).
I've seen some examples here where people use the second parameter of findIndex, but that function never got called for me. It also is not in the docs so I'm guessing it's not supported any more.
With Immutable.js
import React, { useState } from 'react';
import { List } from 'immutable';
export default () => {
const init = [{
id: 101,
interestLevel: 1
},
{
id: 102,
interestLevel: 0
}];
const [myArray, setMyArray] = useState(init);
const updateRow = (e) => {
const id = parseInt(e.target.attributes['data-id'].value);
const immutableMyArray = List(myArray);
const index = List(myArray).findIndex((item) => {
return item.id === id;
});
const myRow = immutableMyArray.get(index);
myRow.interestLevel++;
const newArray = immutableMyArray.set(index, myRow);
setMyArray(newArray);
};
return (
<ul>
{
myArray.map(function (val) {
return (
<li key={val.id}>
<button onClick={updateRow} data-id={val.id} >Update Title</button>
{val.id} : {val.interestLevel}
</li>
)
})
}
</ul>
)
}
Without Immutable.js
import React, { useState } from 'react';
export default () => {
const init = [{
id: 101,
interestLevel: 1
},
{
id: 102,
interestLevel: 0
}];
const [myArray, setMyArray] = useState(init);
const updateRow = (e) => {
const id = e.target.attributes['data-id'].value;
const newMyArray = [...myArray];
var index = newMyArray.findIndex(a=>a.id==id);
newMyArray[index].interestLevel = myArray[index].interestLevel + 1;
setMyArray(newMyArray);
}
return (
<ul>
{
myArray.map(function (val) {
return (
<li key={val.id}>
<button onClick={updateRow} data-id={val.id} >Update Title</button>
{val.id} : {val.interestLevel}
</li>
)
})
}
</ul>
)
}
Upvotes: 1
Views: 356
Reputation: 165
As you mentioned in this comment, you're looking for easier ways of updating objects in a deep object hierarchy using Immutable.js.
updateIn should do the trick.
const immutableObject = Immutable.fromJS({ outerProp: { innerCount: 1 } });
immutableObject.updateIn(['outerProp', 'innerCount'], count => count + 1);
It's also worth noting that you probably want to call Immutable.fromJS()
instead of using Immutable.List()
since the latter won't deeply convert your JavaScript object into an Immutable one, which can lead to bugs if you're assuming the data structure to be deeply Immutable. Switching the code above to use Immutable.fromJS()
and updateIn
we get:
// In updateRow
const immutableMyArray = fromJS(myArray);
const index = immutableMyArray.findIndex((item) => {
return item.id === id;
});
const newArray = immutableMyArray.updateIn([index, 'interestLevel'], interestLevel => interestLevel + 1);
setMyArray(newArray);
Upvotes: 0
Reputation: 19662
Have you considered the purpose of immutablejs?
In your example, you are only adding needless complexity to your code, without leveraging the gains provided by the library.
The purpose of immutable is to provide immutable collections, inspired from scala. In other words, you create your collection, then you pass it to another component, and you can be certain that no element was appended or removed. The individual elements, however, are not under such guarantee, partly due to the constraints (or lack thereof) brought by JS.
As it stands in your code, there are very few reasons to do something like this. I've taken the liberty of changing your quote quite a bit in order to showcase how to do so:
class Comp extends React.Component {
constructor(props) {
super(constructor);
if (props.interests) {
this.state = {
interests: props.interests
}
} else {
this.state = {
interests: Immutable.Set([])
}
}
}
updateRow(e) {
return function() {
this.setState({
interests: this.state.interests.update((elements) => {
for (var element of elements) {
if (element.id == e) {
element.interestLevel++;
}
}
return elements;
})
});
}
}
render() {
var interests = this.state.interests;
var updateRow = this.updateRow;
var list = this;
//return (<div>Test</div>);
return ( <
ul > {
interests.map(function(val) {
return ( <
li key = {
val.id
} >
<
button onClick = {
updateRow(val.id).bind(list)
}
data-id = {
val.id
} > Update Title < /button> {
val.id
}: {
val.interestLevel
} <
/li>
)
})
} <
/ul>
)
}
}
var interests = Immutable.Set([{
id: 1,
interestLevel: 0
},
{
id: 2,
interestLevel: 0
}
])
ReactDOM.render( < Comp interests = {
interests
}
/>, document.getElementById("app"));
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/immutable.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.7.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.7.0/umd/react-dom.production.min.js"></script>
<div id='app'></div>
The changes:
prop
containing interests. This means you can pass a Set
and be sure that it won't have elements added out of the blue within the componentupdate
function is called on the Set
, which is used to update the items inside the collectionrender()
This is both more readable and easier to manage.
Upvotes: 0