I'm using the Map class.
I have a state like this:
{
rows: new Map([])
}
With each row representing an entry in a table. I'd like to update each row independently and asynchronously in a thread-safe manner.
What is the idiomatic React way to perform a setState when each modification is something like this (assume "fooId" has already been inserted into the Map):
const rows = this.state.rows;
const row = rows.get("fooId");
row.status = '75%';
this.setState({
rows: rows
});
I'm using the Map class.
I have a state like this:
{
rows: new Map([])
}
With each row representing an entry in a table. I'd like to update each row independently and asynchronously in a thread-safe manner.
What is the idiomatic React way to perform a setState when each modification is something like this (assume "fooId" has already been inserted into the Map):
const rows = this.state.rows;
const row = rows.get("fooId");
row.status = '75%';
this.setState({
rows: rows
});
Share
Improve this question
asked Jan 11, 2019 at 19:18
NovaterataNovaterata
4,8093 gold badges35 silver badges56 bronze badges
6
- 3 Everything in JS is threadsafe as there is only one thread . Updating a Map (or any other collection) in an immutable way is always difficult as you have to clone the whole Map which might cause lags – Jonas Wilms Commented Jan 11, 2019 at 19:23
- That's your only way of doing it with React, though, so you have no options. And that's assuming it causes latency issues (it might not). – Dan Commented Jan 11, 2019 at 19:24
-
Interesting question...may i ask for my personal knowledge...why do you choose
Map
approach instead ofObject
? – Nikko Khresna Commented Jan 11, 2019 at 19:43 - @NikkoKhresna I'm primarily a backend java / kotlin dev so that might explain it alone lol, but I was under the impression that Map is a better fit when you want to preserve insertion order. – Novaterata Commented Jan 11, 2019 at 19:46
- Map is also a better fit if you don't have string or symbol keys, as objects will coerce keys to strings. – Dan Commented Jan 11, 2019 at 19:51
1 Answer
Reset to default 11The idiomatic way would be to use the functional setState()
since you're updating an existing value rather than replacing it wholesale.
this.setState((prevState) => {
const nextRows = new Map(prevState.rows)
// Creating a new Map this way does not clone the original data
// This means that any update to an object in the map will update the previous map.
// To avoid this, we create a new object using object spread and assign the updated value there.
// This has the benefit of reducing memory allocations as well, if performance is your concern.
// Though it should not be until you have proven it to be a concern.
const nextEntry = {
...nextRows.get('fooId'),
status: '75%'
}
return { rows: nextRows.set('fooId', nextEntry) }
})
This is made a little easier with ImmutableJS:
this.setState(prevState => ({
rows: prevState.rows.update('fooId', entry => ({ ...entry, status: '75%' }))
}))
Also, just need to contradict what @Jonas said: When you clone a map, it only clones the mapping between a key and a value of the map. It does not clone the values of the map. That means that the memory consumption required is a lot lower than you might think.
If you're sure that you won't use the old map or any of the objects referenced in the old map, you could even do away with creating a new object using object-spread and you would only shoulder the impact of copying the key/value mapping (and creating a new object). This could lead to some really confusing bugs if you're wrong (and it would be very easy to be wrong) so I would stick to the immutable thing.
If this really bees a performance issue, you could structure your data in a different way to have even fewer allocations on change.