I'm trying to turn an array in which every element is an array containing the key, and the value(like entries), I'm trying to convert it back to an object element by the help of the method Object.fromEntries(array) but there seem to be a problem.
This is the error I get: TypeError: Object.fromEntries requires the first iterable parameter yields objects
Here's how I use the method:
const saved = JSON.parse(Object.fromEntries(localStorage.getItem(key)))
The local storage value it should retrieve:
[["$$typeof",null],["type",null],["key",null],["ref",null],["props",{"data":"a data containing collections of objects(fetched from an API)"}]
Since it might be helpful, what I'm trying to do is to store a react component(yes, I work on ReactJS) to local storage and get it back when necessary.
If you don't think that this portion of code is not the reason why this issue appears, please do let me know.
Here is a picture of what I get on the console <br><br>
I am using React.js, and the object shown in the image is an element of an array that is part of the state object. I do not understand what is going on. The object shows what is expected of it when collapsed. But when I expand it using the right arrow button, the expanded version of the object gives this output. Can anyone please tell me what's going on here? I want to clarify that I did not use setState to update the state to this condition since I wanted to make sure that I avoid re-rendering.
here is the code for what I did to update the state
if (this.props.currentNumberingScheme === 'a') {
var edge = this.state.edgeList[i];
edge.from.data = String.fromCharCode(parseInt(edge.from.data) + 64);
edge.to.data = String.fromCharCode(parseInt(edge.to.data) + 64);
console.log('GRAPH: updated edge', edge.from.data, edge.to.data);
console.log('GRAPH: updated edge', this.state.edgeList[0].from.data, this.state.edgeList[0].to.data);
console.log('GRAPH: transformed edge list', this.state.edgeList[0].from);
console.log('GRAPH: updated edge', this.state.edgeList[0].from.data, this.state.edgeList[0].to.data);
}
when I log edge.from.data and edge.to.data, the output is in fact 'A' and 'B' (as expected). when I log this.state.edgeList[0].from, the phenomenon shown in the image takes place. Can anyone please clarify??
I'd recommend taking a look at the docs on react state. Though that page doesn't explicitly address the issue you're facing.
The problem is that you define a variable edge from this.state like so:
var edge = this.state.edgeList[i];
edge.from.data = String.fromCharCode(parseInt(edge.from.data)+64);
edge.to.data = String.fromCharCode(parseInt(edge.to.data)+64);
These second two lines modify the edge object but do not update this.state with their altered values. So when you log edge.from.data and edge.to.data you see the correct values, but when you log this.state.edgeList[0].from your displaying data the hasn't been updated.
Again, this is because when you mutate the object returned from this.state it does not mutate this.state.
If you do in fact require an object with direct mutability that does not re-render on change, you could probably use a ref. But likely you should just use a plain ol' object.
So i have an json array named users. Im trying to update an individual field if id comes from request equals to users id. When i use for loop it works but it doesn't when i try to use forEach (there are no errors). I don't understand the difference between these two.
In the forEach, user is just a variable that happens to start with the value passed in as a parameter to your lambda function. Setting user = ... doesn't actually change anything in the original array. Nor does it change the properties on the object that is currently in that array.
Consider using Object.assign() instead:
Object.assign(user, req.body);
I've been going through the docs, but I don't see a way to update a value at a specific key (while keeping the rest). Is the only way to achieve this is to fetch the existing data and overwrite it using set()?
I have something like this:
firebase.database()
.ref(`/users/${currentUser.uid}/products/${productKey}`)
.set({category: name});
where products have many other key-value pairs, and using set() overwrites the entire object, which is not what I want in this case.
I appreciate any help!
From the section Update specific fields:
To simultaneously write to specific children of a node without overwriting other child nodes, use the update() method.
In your snippet that would be:
firebase.database()
.ref(`/users/${currentUser.uid}/products/${productKey}`)
.update({category: name});
When I do a push or pop operation on my observable array, it is reflected in the ui. However other operations on the array won't change anything in the UI. Here's an example of my case:
<ul data-bind="foreach: addresses">
<!-- ko template: {name: 'AddressItemTemplate', data: {address: $data, page: 'update-page'} }-->
<!-- /ko -->
</ul>
I use my template in two different pages and thats the reason I am using the template data like that.
<script type="text/html" id="AddressItemTemplate">
<p data-bind="text: (page == 'update-page') ? 'updating' : 'declined'"</p>
<p data-bind="text: address.title"></p>
</script>
Now on js side, ofc I declared the addresses as an observable array
this.addresses = ko.observableArray([addresObject1, addressObject2, ...])
Somewhere on the page, I edit the address values. To have UI reflecting the changes, I do the following:
//suppose we know that the first address is being edited
var tmp_addresses = addresses();
tmp_addresses[0].title = 'blabla';
addresses(tmp_addresses);
And there it is, in the viewModel, I can see that the content of the addresses has been updated, but not in the UI??
addresses.push(someAddressObject);
or
addresses.pop();
works (updates the UI with the new/removed element). But addresses.splice(0, 1, newAddressObject) does not do anything in the UI again.
What am I missing here? How can push pop work and not the others??
Am I experiencing a bug in knockout framework?
UPDATE
I found out a way to do it, but there's something wrong. I'll come to that but first:
I am well aware that if I use observable objects in the observable array, the changes would be reflected in UI. However that is exactly the thing I want to avoid. It is an overkill.
Observable properties should be required in cases where properties are really exposed to user interaction. For example, if you have a UI for setting each of the fields of an object, then yes, observable property would be the right call.
However in my case, I dont even have a UI for updating the address field. Moreover, I dont need tinkering and constantly watching all the properties of all the addresses. In my case, every now and then an update occurs from the server and that changes only a single field in a single address field.
On another perspective the way I suggest should work. I simply update the whole array at once, not every element individually. It's the exactly the same logic with:
someObservableObject({newObject: withNewFields, ...});
Thats why I dont need my objects as observables. I simply want to re-declare the array and be done with the change. For example, it is advised that if you are going to make lots of pushes into the observable array, dont use array.push(...) multiple times, instead re-declare the larger array on to the observable array variable in a similar way I do it in my question. Otherwise, I am telling knockout to track every single object and every single field in them, which is hardly what I want.
Now, I finally got it working but the way I do suggests that there is a cleaner way to do it.
I found out that, the items in the observable array are somehow tracked and not updated when you re-declare the array with them. For example the code I gave in the question would not work. However the code below works:
var tmp_addresses = addresses();
var tmp_addr = tmp_addresses[0];
var new_addr = {};
Object.keys(tmp_addr).forEach(function(key){
new_addr[key] = tmp_addr[key];
});
new_addr.title = 'Hey this is something new!'
addresses.splice(0, 1, new_addr);
Not satisfied? The code below is going to work as well, because we are re-defining the array:
var newAddressObject1 = {...}, newAddressObject2 = {...};
addresses([newAddressObject1, newAddressObject2]);
But the following would not work!
var tmp_addresses = addresses();
var tmp_addr = tmp_addresses[0];
tmp_addr.title = 'Hey this address wont update';
addresses.splice(0, 1, tmp_addr);
How come? I think knockout adds an internal property to his items in observableArrays and when I try to reinsert one, it will not update.
My problem has now morphed into creating a new object with the same properties of the desired item in the observable array. The way I coded above is simply very dirty-looking. There's gotta be a better way to do that
You are wrongly assigning value to observable title that is the reason why UI not reflecting its changes (2 way binding broken).
Thumb rule is always use () notation while assigning a value to observable (keeps two way binding intact)
viewModel:
var ViewModel = function () {
var self = this;
self.addresses = ko.observableArray([{
'title': ko.observable('one')
}, {
'title': ko.observable('two')
}])
setTimeout(function () {
var tmp_addresses = self.addresses();
tmp_addresses[0].title('blabla'); //assigning data to observable
self.addresses(tmp_addresses);
}, 2000)
};
ko.applyBindings(new ViewModel());
working sample here
PS: Don't get deceived by seeing the value change in viewModel the moment you done assigning using = two binding is broken UI wont reflect VM'S changes .
when you splice up your observableArray UI takes it changes check here
The problem was exactly as #jason9187 pointed out in the comments: The references of the objects in the observable array does not change when I edit a field of them. Therefore, KO would not interpret my array as changed. If the observableArray had contained simple data types, then the way I suggested could work without a problem. However, I have an Object in the array, therefore although I edit the Object, it's reference (pointer) remains the same, and KO thinks that all Objects are the same as before.
In order to achieve what I wanted, we have to solve the deep cloning problem in javascript like in this post.
Now there's a trade-off there, deep cloning is very simple in vanilla if you don't have a circular architecture or functions in your objects. In my case, there's nothing like that. The data comes from a restful API. If anybody in the future gets hold of this problem, they need to deep-clone their 'hard-to-clone' objects.
Here's my solution:
var tmp_addresses = JSON.parse(JSON.stringify(addresses())); //Creates a new array with new references and data
tmp_addresses[0].title = 'my new title';
addresses(tmp_addresses);
Or, if you can create address objects, following will work as well:
var tmp_addresses = addresses();
tmp_addresses[0] = new randomAddressObject();
addresses(tmp_addresses);
Here is a fiddle that I demonstrate both of the methods in a single example