Object values are changed as soon as I open it - javascript

I am working with Javascript/ReactJS code, I am printing the Object(Obj), containing keys as ids and values as the status(true/false) of the corresponding keys in the console. The desired Object is printed, but as soon as I opened that object, it automatically changed its values.
the first property of this object is true, but when I open it, it goes false.
I declare that Object(Obj) outside the render method because it renders JSX every time either it needed or not. But it is not working.
render(){
return defaultApp(templates).map(template=>{
Obj[`${template._id}`] = false;
return <Apps id={template._id}
onClick={this.handleToggle.bind(this)}>
<h2>{template.title}</h2>
</Apps>
});
}
handleToggle = (e)=>{
var selectedAppId = e.target.id?e.target.id:e.target.parentNode.id;
if(!isEmpty(Obj)) {
Object.keys(Obj).forEach((id)=>{
if(selectedAppId===id){
Obj[selectedAppId] = !Obj[selectedAppId];
console.log(Obj);
this.setState({currentAppId:id,AppsStatus:Obj});
}});
}
I want to see the same Object's values either I open it or not.

console JSON.stringify(obj) to check the exact object values.
Remember objects are references so if their key values are changed at any point of the program, the change will be reflected all over the code as all the variables are storing the reference to that object.

Related

Array in object comes back as undefined when accesing directly

This is a React Component which was given an array of objects(main_object) in which one of it's elments was another array of objects(secondary_object). When printing the main object in console.log the array is visible but when trying to print the array of secondary objects it returns undefined but if I access another variable of the main object it returns it.
Code:
render(){
const fleets = this.props.fleets;
console.log(fleets[1]);//works
console.log(fleets[1].name);//works
console.log(fleets[1].ships);//undefined
}
Output:
Console Output
Edit: Used my actual code instead of example code
It is difficult to recreate your environment. Here I mimicked the this by referring the global window object. And, as you can see, it works in the way that the .ships property is listed as the array it actually is.
window.props={fleets:[
{},
{f_faction:1,
id:1,
members:["one"],
name:"Capital Fleet",
owners:["owner1"],
ships:[{a:1,b:2},{a:5,b:4},{a:7,b:8}],
x:789, y:2897, z:-23}
]};
function render(){
const fleets = this.props.fleets;
console.log(JSON.stringify(fleets[1]));//works
console.log(JSON.stringify(fleets[1].name));//works
console.log(JSON.stringify(fleets[1].ships));//lists array!
}
render()
#trincot's advice that console.log output of objects is asynchronous might be very relevant here. Therefore, if you want to get the content of an object at a specific time, you should take a "snapshot" of it. One way of doing that would be through JSON.stringify().
try this out
render(){
const fleets = this.props.fleets;
console.log(JSON.stringify(fleets[1]));
console.log(JSON.stringify(fleets[1].name));
console.log(JSON.stringify(fleets[1].ships));
}
By using JSON.stringify I was able to see that the problem was with the async creation of the array.
I fixed up all my async code and now it's working fine.

How can I make a value in Vue.js non-reactive?

In my Vue.js app, I have an array value that should only be updated when a user completes a specific "refresh" action. However, as soon as I assign a new value to that array value, the array value becomes reactive and changes instantly as the data in the assigned value changes. The array value should remain un-reactive.
For example, I have a method, refresh(), which when triggered is meant to update displayedData, which should not be reactive, with currentData, which should be reactive. displayedData should only update when refresh is called.
methods: {
refresh: function() {
this.displayedData = this.currentData;
}
}
To make a value not reactive without making it static, you can make a "deep copy" of it using stucturedClone(), which is becoming widely supported as of 2022:
this.displayedData = structuredClone(this.currentData);
Another widely used method is to use JSON to encode and then decode it:
this.displayedData = JSON.parse(JSON.stringify(this.currentData));
Both of these methods assign the current state of one value to another value, and no changes to the first value will change the second value until this code is triggered again.
The reason this is necessary isn't because of Vue.js specifically, but because of JavaScript in general. In JavaScript, arrays and objects are "passed by reference" rather than "passed by value".
An approach (WITHOUT disabling reactivity) is to use a different array for your temporary data and move stuff over in to the good array when the user presses refresh.
You could copy the values in the first array into the temp array like this.temp = this.permanent.slice() in the created or mounted life-cycle function.
Slice would make a shallow copy of the array. If you need to also clone the items in the array, then maybe use some deep copy library or the JSON.parse(JSON.stringify(...)) method.
If you DON'T add the currentData to the data() method then it will not be reactive.
export default {
currentData: [],
data() {
return {
}
},
methods: {
refresh: function() {
this.displayedData = this.currentData;
}
}
}
You can then still reference currentData in the section using {{ $options.currentData }}
You can use destructuring assigment:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment?retiredLocale=it
this.displayedData = {...this.currentData}
You will have a new object made with the "copied" data from the first

chrome.storage.local.get an object and iterating through obj keys

I'm having trouble using the chrome.storage.local.get feature. I have all working, except I don't understand how to handle the .get function.
I want to copy an object by clicking a button, from one chrome tab to another one. I have a loop to get my Object keys, and I can save it successfuly in chrome local storage by:
chrome.storage.local.set({
'copiedObj': obj
});
but, when trying to use the .get function, I can't specify the key number I want. I need the key number because the keys and their values changes every time because i'm copying data from different pages.
$('#targetInfo').click(function(){
console.log('context 2 received');
var storage = chrome.storage.local.get('copiedObj', function (items) {
console.log(items);
});
});
this gets me the full object keys and values, but when trying to change it to get the first key (changing the console.log inside the script to items[0], I get an undefined error message in the console.
I want it to iterate through all the keys, and assign key to a variable, value to a variable and execute a function. For each key by it's time. I've written a detailed explanation inside the code:
//Get the DOM input field
var specific_name = document.getElementById('new_specific_name');
var specific_value = document.getElementById('new_specific_value');
//Start loop, from 0 to the last object keys
for (i=0;i<items.length;i++) {
//Set specific_name value to the first key inside the object
specific_name.value = XXX[i];
//Set specific_value value to the matching key value inside the object
specific_value.value = ZZZ[i];
//Execute this function
add_new_specific();
}
(minimal solution, pending clarification) As mentioned before,
var storage = chrome.storage.local.get('copiedObj', function (items) {
console.log(items);
});
should be
var storage = chrome.storage.local.get('copiedObj', function (result) {
console.log(result.items);
console.log(result.items[0]); // if items is an array...
});
To loop through an object, see https://stackoverflow.com/a/18202926/1587329. You need to do this all inside the anonymous function in .get.

How do I prevent my program from overwriting localStorage every time a button is clicked?

document.getElementById("submit").addEventListener("click", getElements)
function getElements() {
var a = document.getElementById("sample").value;
var x = new obj(a);
function store() {
localStorage.setItem('todays-values', Object.values(x));
}
store();
}
In a separate js file I then call
localStorage.getItem('todays-values');
I get the values, but if I put new inputs into my html file and click the submit button, the previous values get overwritten and replaced by the new ones. How do I store all the values that are submitted and prevent the old ones from getting replaced?
I'm very new to Javascript so I would prefer to solve this problem without the use of any additional libraries if possible.
First: it seems that you are mixing JavaScript a class with a function (here is an example: What techniques can be used to define a class in JavaScript, and what are their trade-offs?)
For example this is the class equivalent in JavaScript:
function ClassName() {
var privateVar;
this.publicVar;
function privateFunction() {}
this.publicFunction = function() {};
}
You shouldn't wrap a function in a function unless it has a meaning (beacuse it is confusing for other people otherwise), but in the example given you don't need that. Also I can't see the reason why you are creating a new object x - if you create the object right before you save it you could just save the value because the object will only contain the value from sample, so you could write something like this:
document.getElementById("submit").addEventListener("click", getElements);
function storeElements() {
var sampleValue = document.getElementById("sample").value;
localStorage.setItem('todays-values', sampleValue);
}
Back to your question:
As Kalamarico mentioned: if you write new values into todays-values you will overwrite your old values, you could simply load all old values from the localStorage append the new ones and write them back to the localStorage.
You should also note that the localStorage only takes strings, so you should stringify objects (see localStorage.setItem).
function appendValueToStorage(key, value) {
var values = JSON.parse(localStorage.getItem(key));
if (values === null) {
values = [];
}
values.push(value);
localStorage.setItem(key, JSON.stringify(values));
console.log(localStorage.getItem(key));
}
appendValueToStorage('todays-values', document.getElementById("sample").value);
The function will let you append some value for a key, you could even wrap this function again to be able to use it in your click function:
function onSubmitClick() {
appendValueToStorage('todays-values', document.getElementById("sample").value);
}
document.getElementById("submit").addEventListener("click", onSubmitClick);
With the console.log command you can see the current content of the localStorage (you could also check with the developer tools - I find the ones for chrome work the best, under the Application -> Local Storage tab you can check the localStorage of your page).
You need read more about localStorage, this is a new feature introduced with HTML5, you can take a look here and see all features.
localStorage stores your data like a JSON object, if you don't know what is JSON, you need to find info. In javascript think in objects in this way:
var myData = {
myName: 'Kalamarico',
myAge: undefined
};
This is a Javascript object, and JSON is very similar and it is a representation of objects.
localStorage API stores your data as this way, when you do:
localStorage.setItem('todays-values', Object.values(x))
localStorage saves a new entry, one key 'todays-values' and its value is an object, so, your localStorage seems:
{
"todays-values": { ... }
}
Every time you set a "todays-values" you will overwrite the key, as you are seeing, so, if you can keep old values, you need to do this manage, first you can get items in localstorage (if there are), and after you can "merge" your old value and the new value. Or you can set a new key, for example: "todays-values1" depends on your need.
If you need to store exactly one key-value pair per day, then you could add the date in the key string.
Else how about numbering the keys ("yourKey_0", "yourKey_1", ...) and also storing the current (biggest) index ("currentIndex")in local storage:
function store(value) {
newIndex = localStorage.getItem("currentIndex") + 1;
localStorage.setItem("yourKey_" + newIndex, value);
localStorage.setItem("currentIndex", newIndex);
}
If you run into problems storing integer values, convert to strings.

Changing local variable in JavaScript affects original global with different name

I have a global declared at top of script:
var g_nutrition_summary = null;
When the user enters the page, I return network data and give this variable a value.
g_nutrition_summary = json.data;
This line is the ONLY assignment of the variable and is never called again (tested with alerts).
I later use that json.data variable to populate a Bar Chart with the plugin Chart.js. The global assignment is for later use.
Underneath the chart, the user can filter certain types of data it displays with a series of checkboxes. So my goal is, to keep an original value of what comes in from the network, and then make a LOCAL COPY of it and alter the COPY (not the global original) and repopulate the chart. Everytime the user checks/unchecks a checkbox, it will call this function and grab the ORIGINAL global (g_nutrition_summary) and re-filter that.
Here is how I do it:
function filter_nutrition_log()
{
alert("1: " + JSON.stringify(g_nutrition_summary));
// reassign object to tmp variable
var tmp_object = g_nutrition_summary;
var food_array = new Array("Grains", "Vegetables", "Fruits", "Oils");
var checked_array = new Array();
// Make an array of all choices that are checked
$(".input-range-filter").each(function()
{
var type = $(this).val();
if ($(this).is(':checked'))
{
checked_array.push(type);
}
});
alert("2: " + JSON.stringify(g_nutrition_summary));
// Loop thru all the 7 choices we chart
$.each(food_array, function(i, val)
{
// find out if choice is in array of selected checkboxes
if ($.inArray(val, checked_array) === -1)
{
// it's not, so delete it from out tmp obj we
// will use to repopulate the chart with
// (we do not want to overwrite the original array!)
delete tmp_object["datasets"][val];
}
});
// Resert graph
alert("3: " + JSON.stringify(g_nutrition_summary));
getNutritionChart(null, tmp_object, null, false);
}
Somehow, between alert "1" and alert "2". The global gets changed. Then when the user clicks a checkbox again and it calls this function, the very first alert shows that the original, global object contains the altered data to the tmp_object variable.
As you can see, I call a third party function I have created when this happens originally. Doing a search for the global there is absolutely nowhere else it is used in the instances described above.
Am I not understanding something about JavaScript variable scope?
Both objects and arrays in javascript are treated as references, so when trying to pass them to functions or to "copy" them, you are just cloning the reference
To have a "real copy", you would need to traverse the object and copy its content to another object. This can be done recursively, but fortunately jquery already comes with a function that does this: $.extend
So the solution would be:
var tmp_object = $.extend({},g_nutrition_summary);
If you have a nested object, you need to set an extra parameter:
var tmp_object = $.extend(true,{},g_nutrition_summary); // now it will do it recursively
For arrays, an easy way to make a "real copy" is, as #Branden Keck pointed out,
var arrCopy = arrOriginal.slice(0)
More on jquery extend: https://api.jquery.com/jquery.extend/
Going along with juvian's comment. To create the new array as somewhat of a "copy" and not just a reference, use:
var tmp_object= g_nutrition_summary.slice(0);
However, .slice() is only works for arrays and will not work on JSON, so to used this method you would have to create an array from the JSON
Another method that I found (although not the cleanest) suggested creating a string from the JSON and re-parsing it:
var tmp_object= JSON.parse(JSON.stringify(g_nutrition_summary));

Categories

Resources