I'm making a simple react native app that has several lists and it gives the user the opportunity to update an item in any of those lists or to add new items to the list.
However, as has been previously pointed out on StackOverflow, one can't add or push an item to a ListView because it's supposed to be an immutable data structure. i.e. in this code one cannot push a new item into the data source
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
var data = ds.cloneWithRows(responseData)
var obj = {};
obj['someproperty'] = 'foo';
data.push(obj); //cannot push a new item into data
So what I've done instead is to keep a copy (this.state.items;
in code below) of the data I fetch from the server (and which I call cloneWithRows
on), append to that copy (or update it if it's an edit), and then call cloneWithRows
on the mutated copy any time there's a change (either through an edit or creation of a new item) and update the ui, and then update the server (this last part outside the scope of the react native app).. So in summary, I have a copy of the data that I can mutate to use as a source of data for ds.cloneWithRows
when I make a change
However, it doesn't seem to make much sense to keep a cache of the data structure that I can mutate for the sole purpose of using it as the source for the immutable data structure (listForUI: ds.cloneWithRows(t)
in the code below) if I want to optimistically update the UI (i.e. show the change before its saved on the server).
The alternative (also not making much sense to me) would seem to be to update the server, and wait for a response (without keeping a cache of any kind) any time I update or add a new item to my list, but this would be slow?
Question: Short of keeping a mutable copy of the data as a cache, is there a way to optimistically update a ui in react native?
addNewItemToList: function(){
var t = this.state.items; //my cache of list items
var newListItem = {};
newListItem['id'] = 123;
t.push(newListItem); //adding-mutating the cache
//creating a new immutable ds
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.setState({
listForUI: ds.cloneWithRows(t)
}, function(){
//update server with change
fetch('http://localhost:8080/accounts/1/lists/3/todos', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
//code ommitted
}
}
No, I'm pretty sure that there isn't any other way to update a ListView datasource, since as you explain, it's immutable. Immutability implies that you will always need to make a copy of anything before adding to it.
You mention:
However, it doesn't seem to make much sense to keep a cache of the data structure that I can mutate for the sole purpose of using it as the source for the immutable data structure...
I'm not really sure why you feel that keeping this cache doesn't make sense. I don't see it being a cause for performance problems either, since the cache only exists for a brief moment while addNewItemToList
is being run.
You will need to store the previous items if you want to undo an optimistic update. To do so you will need to "cache" or store a reference to the previous data somehow.
You can optimistically call this.setState
in your addNewItemToList
handler and update your state (causing a re-render), and then in your fetch
you can revert the change if necessary (ie if the call fails):
addNewItemToList: function(id) {
// Store current list of items and calculate new list
var prevItems = this.state.listForUI.slice();
var newItems = this.state.items.concat([id]);
// Optimistically set new items list; will cause a re-render
this.setState({
listForUI: this.state.listForUI.cloneWithRows(newItems)
});
// Update server with change
fetch('http://localhost:8080/accounts/1/lists/3/todos', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
//code ommitted
})
// Catch an error and revert change
.catch(function (ex) {
this.setState({
listForUI: this.state.listForUI.cloneWithRows(prevItems)
});
});
}
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