In my React app, at one point I'm trying to copy a Map from an object, but it's not working for me.
The initial user object has the structure:
user: {
videoTracks: Map,
... other fields...
}
If I do a console log on user, I get this for videoTracks:
Then I do this: const newVideoTracks = user.videoTracks;
and I would expect newVideoTracks to be the same as the first image, but instead I get the following:
It has 2 entries and size:2, but it says Map(1) at the top.
I know the console isn't always accurate, but in this case when I subsequently use newVideoTracks, it behaves as though there is only 1 entry.
I assume I need to do some cloning rather than just copying it, so I tried both of the following:
const newVideoTracks = new Map(user.videoTracks); - this didn't make any difference
const newVideoTracks = Object.assign({}, user.videoTracks) - caused a crash
What's the correct way to do this?
Related
I am facing strange behavior with useState. In the sandbox https://codesandbox.io/s/elastic-ellis-gjre7 I have 3 input fields, that change value if you click on them. The problem is with history state, that I log in console.
Expected behavior is, that as soon as you click on 1 of the boxes, it saves the state of all 3 boxes in history array, when we click another, it adds to history array new placements.
When clicking 1 box, it works correctly - "History 1" shows placement, and "History 2" and "History 3" are undefined.
But when we click 2nd box, it starts to act strange - It rewrites previously written "History 1", so now the content for "History 1" and "History 2" are the same. Strange, because all we do is concatenate new value to history array, but it overwrites the previous values.
Can anyone explain, why does it act in such a way?
On line 16, newValue[event.value.id] is getting mutated. Meaning, the same value is being updated and referenced in each element of history.
To reach the expected behaviour, the solution is to reference different values. This can be achieved by way of cloning.
See this fork of your sandbox for a practical example.
// Mutation.
newValue[event.target.id].value = "X";
// Cloning.
const targetId = event.target.id
newValue[targetId] = {...newValue[targetId], value: "X"};
You should write it as the following:
setHistory((prevHistory) => prevHistory.concat({ ...placements }));
Explanation:
I believe what you are doing is you're merging the previous state with the current one, from what I understand you want to contact the arrays -correct me if wrong -
You have several things wrong.
You are using item.id as event.target.id, but you are updating base on placements' array index, which you will eventually end up with unforseen results.
You are updating history base on placements state, but when you are updating the history, the new placements might not be ready yet.
Here we will update your placements base on your item id instead of index. Then we update the history at the same time with the new placements.
const handleClick = (event) => {
// .map is commonly used in this type of situation to update the correct object in the array with new value.
const newplacement = placements.map(item => item.id === parseInt(event.target.id) ? { ...item, value: 'X'} : item )
setPlacements(newplacement);
setHistory((prevHistory) => [...prevHistory, newplacement]);
};
my sandbox
Okay guys so I've run into a problem on some code I've been working on. In the code below, I'm trying to console log the length of an array, but after printing to the console, I get an output of 0 for the array's length, and when I print the array itself, it shows that it is empty, but I can click the drop down arrow and see the element inside it.
// randomly selects card from array
export const selectCard = arr => {
const randomArr = Math.floor(Math.random() * arr.length);
console.log(arr.length);
console.log(arr);
}
Image of logged array length and empty array with an element inside.
To test what was happening, I decided to console log the array in a different function that is in the same file where the array is created. The array (blackCards) is stored in an object.
let packArr = {
data: {},
packToUse: [],
whiteCards: [],
blackCards: [],
};
Console logging the array (blackCards) in another function logs this:
const seperatePacks = (data) => {
// add white cards to array
packArr.whiteCards.push(data.whiteCards);
// add black cards to array
packArr.blackCards.push(data.blackCards);
// console log blackCards array
console.log(packArr.blackCards);
};
Image of logged array from a different function which is within the same file where the array is created and stored.
Here's a little more about how my code works. When a user clicks on a button, an event listener is activated, which calls other functions that eventually get json from a sever, then I add that data to the array, which you see in the code above. So now that you know how the code works, I can get to what is causing me to be even more confused. So, on the first click, I get the outcome of all the previous code and images combined, but if i click on the same button again, or click on a different one, I get the desired outcome (if I click on two button I get two elements in the array, so it's working correct). So, two or more clicks and the program works the way it should. Any help here would be great. Also sorry if this post was a little scuffed, it is my first one.
Here are the functions that eventually call selectCard
elements.packs.addEventListener("click", (e) => {
const packID = e.target.closest(".pack").id;
if (packID) {
packsToUse(packID);
// create black cards
blackCardDisplay(packArr.blackCards);
}
});
Above is the listener that was mentioned earlier, which calls the function below, that will call selectCard.
export const blackCardDisplay = data => {
const card = `
<div class="cards card--black">
<h1 class="card--black-text card--text">${selectCard(data)}</h1>
</div>
`;
}
Here is the output after pressing two buttons. The array should contain two elements but in this case it only shows one. However when I expand the array, it shows a length of two instead of just one.
Console image of two button clicks
EDIT: After reading some of the comments I realized I forgot to mention that the contents of blackCards is an array. So I have an array within an array.
So, after reading some comments I decided to implement #charlietfl's comment that maybe the array was console logged before the ajax call had been completed. Although this did not explicitly fix the problem, it set me on the right track to the solution.
Here is how I solved this problem:
I took the comments given about the console log completing before the ajax call could finish, and from there, I did some research and found this very simple article:
https://www.programmersought.com/article/26802083505/
In this article was an example using the setTimeout() method.
Here is the final solution to my problem.
export const selectCard = arr => {
const randomArr = Math.floor(Math.random() * arr.length);
setTimeout(() => {
console.log(arr.length);
console.log(arr)
}, 1000);
}
Here is the output after adding the setTimeout() method on the first button press.
Image of solved array length and array console log
I'm not aware if this is the best solution for the problem, because I've seen some people disapprove of using this method in the past, but it will work fine for what I'm doing at this point. If anyone has a better way of solving this I would love to hear it!
I've got a strange situation in my typescript file where these two console.info calls show separate values for overage:
this.rows[index].overage = 17;
console.info(this.rows[index].overage);
console.info(this.rows[index]);
The first printout shows the expected value of 17. The second, where the whole object is displayed, show the old value of 90 for overage. How is that possible?
Rows is defined like so:
rows: UsageDisplayData[];
export interface UsageDisplayData {
id: number;
overage: number;
// A bunch of other properties
}
It's hard to say without an MVCE exactly what is happening, but the expanded view in the dev console is usually a "live" view and thus will show whatever the latest value is. Example:
let obj = { m: 17 };
console.info(obj.m);
console.info(obj);
obj.m = 90;
Few mins ago I did this answer and the answer snippet is below
let obj = {staff_changes: []};
let newStaff=[];
for (let i = 0; i < 4; i++) {
newStaff.push({id: 'staff' +i});
obj.staff_changes.push({
id: i,
newStaff: newStaff
});
}
console.log(obj);
If you run this above snippet, you can see /**id:4**/ and /**ref:4**/ . What is this?
When the code on execution time, that was pushing same duplicate values into a array. So I hope at the starting time it's generating a Id:4 and if the same duplicate value will exist, then just it write a comment like /**ref:4**/ where 4 means Id=:4 which is generated already.
So I want to know Is my understand is correct?. If my understanding is correct , then how can we avoid this? Shall I use object.assign() before push the value into array to avoid this?
Your data structure contains multiple references to the same object. console.log is intelligent enough to abbreviate the output.
Note that (AFAIK), the specification does not guarantee any particular output from console.log for objects that aren't instances of String, so you cannot rely on that output being the same across browsers, versions, phases of the moon, etc.
Consider an infinitely recursive data structure like const a = []; a.push(a); console.log(a), which one would you prefer: your computer to lock up while printing an infinitely recursive array or console.log abbreviating it?
const a = []
a.push(a)
console.log(a)
// [
// /**id:1**/
// /**ref:1**/
// ]
Depending on your console tools, they will display an object like this in different ways. Those comments are telling you there is more information deeper in the object.
If you want to see the internals in a consistent way, you can stringify the whole object
console.log(JSON.stringify(obj));
in which case you get:
{"staff_changes":[{"id":0,"newStaff":[{"id":"staff0"},{"id":"staff1"},{"id":"staff2"},{"id":"staff3"}]},{"id":1,"newStaff":[{"id":"staff0"},{"id":"staff1"},{"id":"staff2"},{"id":"staff3"}]},{"id":2,"newStaff":[{"id":"staff0"},{"id":"staff1"},{"id":"staff2"},{"id":"staff3"}]},{"id":3,"newStaff":[{"id":"staff0"},{"id":"staff1"},{"id":"staff2"},{"id":"staff3"}]}]}
In some developer tools, you can expand the object when you log it to the console, but the above string output shows you the whole lot consistently across tools.
I have defined a function called Node which stores the properties of nodes in a graph data structure. The function is something like this:
function Node(){
...
this.outEdges = [];
this.inEdges = [];
...
}
where the inEdges and outEdges store elements of type Edge which is another function I have defined. During the program these arrays are filled with elements.
At some point in my code I need to reset these two arrays so I write:
nodes[i].outEdges.length = 0;
nodes[i].inEdges.length = 0;
where nodes is an array of elements of type Node and I am accessing an element in a for loop.
The problem is, after setting outEdges and inEdges to 0, I expected them to be [] in the nodes[i] property list. However, when I output nodes[i] into console, the outEdges and inEdges still have the elements in them. The stranger thing is that when I output nodes[i].outEdges to console, it prints [] , which is correct, but clicking on [ ] again opens the list of the elements! I can't really figure out why the nodes[i] variables don't change?
That happens (probably) because the browser prints out the empty array but by the time you check it, it has content again. So when you click to expand the browser shows the actual content.
As you can see the values [1,3,7] were added after the command console.log(o) but they are shown on the screen (even though the length shown is 0).
You're not supposed to set the length field. Just re-initialize them:
nodes[i].outEdges = [];
nodes[i].inEdges = [];
Edit: My bad, setting the length should work. It does work for me on Chrome at least. However, I still think it's safer and better style to re-init.
Just create a new object with the same name
nodes[i].outEdges = new Array();