ReactJS - Can anyone explain how and why this state is being altered? - javascript

I am attempting to make Conway's Game of Life with React. I have an array of boolean values representing the grid in state, and then render an "alive" or "dead" cell for true or false values.
The solution I came up with for counting the surrounding number of "alive" neighbours to determine whether a cell is alive or dead in the next generation includes treating corner and edge cells separately from the rest of the middle cells.
In order to do this, I tried making a copy of the grid array and set the elements representing the corner and edge cells to null after counting their neighbours. Then, I was going to iterate over the remaining middle cells and count their surrounding neighbours.
However, after changing elements to null in the copy of the grid, the original grid in state is also changing. Can anyone explain this behaviour?
console.log(this.state.grid[topLeftCorner]); // false
innerSquare[topLeftCorner] = null;
console.log(this.state.grid[topLeftCorner]); // null
https://codepen.io/Egeroth/pen/jaeYwR

When you assign a existing array to another variable, you do not get a copy, simply two variables which point to the same array. If you want to create a separate copy then consider using array.slice() and see Copying array by value in JavaScript for more details.

In the countNeighbours() method you are creating copy of the gird in a wrong way:
let innerSquare = this.state.grid;
This will copy only reference to the grid, not clone it.
You should write: let innerSquare = this.state.grid.slice(0); instead.
Adding .slice(0) will actually clone the grid into a new array.

Related

ReactJS - Duplicated objects in state array remain linked

I've reproduced the issue in the following codesandbox:
https://codesandbox.io/s/unruffled-danilo-mbb0e?file=/src/App.js
I have a state array of objects [{name:"Tom"},{name:"Dick"},{name:"Harry"}].
I want to be able to duplicate the object present at a specific index in the array. I've provided a button "Duplicate" to do so in the sandbox.
Follow the following steps in the sandbox to recreate the issue:
Click Duplicate under "Dick" to duplicate the Dick object in the state array
Now click "change" under one of the two Dicks. Notice that my code only changes one "Dick" object in the state array, but the other duplicate one automatically gets changed.
I want to avoid this. I don't want the two Dick objects to remain linked forever. How do I do this?
Your'e shallow copying the array. You need to deep copy.
Replace let copy = cur.slice(); with let copy = cur.map(item => {return {...item}}); and your code should work. basically we need to destructure the inner object to get a new copy of the every object in the array. You can read about this here Object Immutability in JS

How do I capture indices when a value is assigned in 2D array?

I have a 2D array. As you might expect, it is composed of an array of rows with columns in the form of a second array in each row. When I assign a value to a particular [Row][Column] pair, I would like to have a function that can capture the indices for the element that has been modified and then be able to do something with them.
I have tried to use a proxy, but I can only make it intercept changes that are made to the Row, not the combination of Row and Column.
i.e.
row[3] = {1,2,3,4}; //works!
row[3][2] = 42; //Does not work :/
I have searched extensively in SO, W3Schools, Google, etc. but I cannot find anything that addresses this specific requirement.
var row=new Array();
for(var loop=0; loop<10;loop++) //Create 10 rows
{
row.push(new Array(10)); //10 columns per row for the sake of example
}
row[0][0]="Added a Value at (0,0)";
row[3][7]="Added a value at (3,7)";
console.log(row[3][7]); //outputs "Added a value at (3,7)" as expected.
This works fine and I'm happy with being able to manage data in this grid construct. I would like to be able to capture when a value is assigned and have access to the two indices so I can perform validation and subsequent activities. Any guidance would be greatly appreciated.
Proxy getter/setters only respond to top level property changes. See https://github.com/geuis/bodega/blob/master/src/store.js
Alternatively, you could assign a new Proxy object that has a getter/setter for a property like row and/or column in each parent array index. That would let you detect when one of those properties is updated.

How to keep track of the sort index with Knockout-sortable?

I'm using Knockout-sortable to drag-and-drop/sort records in my table, but I've run into a problem. I have no clue how to keep track of the position in the sort index of an element. (I.e. element A, B and C appear in that order and have 1,2,3 as index respectively, but if B gets dropped above A the correct index would be 2,1,3)
Nothing in my code is custom: I just include knockout-sortable and it's plug and play. I usually always include a code snippet, but I don't feel that's useful. The only thing I know is that I'm probably gonna need a ko.computed(), but I have no idea what to fill it in with.
If you look at example http://jsfiddle.net/rniemeyer/Jr2rE/, you can see that the plug-in works by updating an observable array of data. Because of this, you don't have to keep track of the index value. The order of the records, technically, gives you all the information you need.
That being said, I ran into the same issue in last year. To solve my problem, I added a consecutively numbered index property to each object in my observable array. Then, when the sortable plug-in re-arranged the contents of the observable array, I just had to read out the new index property to know the sort order.

How to assign values to objects through a variable that is just a reference to them?

I couldn't really word the question less vaguely, but I think you will understand...
I am developing a game engine in Javascript, and the Scene object, which is a container of many things, has a method that is supposed to change one array in it, specifically the one holding all the things that can be drawn.
This array is accessed like this:
scene.internals.draw
The problem is, it is referenced many times in the method, and I think that the name/path might change. Naturally, I don't want to change every reference to it in the method each time I change the the array's path, so I did this:
var location = scene.internals.draw;
Now, the actual method code and the algorithm can stay intact, and if the name/path of the array in the scene changes, I only need to change that one line.
And it works pretty well for the most part. I can .push(obj) to it, etc, but at one point, I need to "disect" the array, ie, split it in half, add something, and then put it back together, like this:
buff1 = location.slice(0, i); //First slice of the array.
buff2 = location.slice(i, location.length); //Second slice of the array.
//Add something in between those slices.
buff1.push(ob);
location = buff1.concat(buff2); //Problems here!
This worked well while location was just scene.internals.draw, as it changed the array directly. But now, I assign the new value to the local location variable, not the desired scene.internals.draw one!
Question: how can I, using the = operator, assign values to "real" objects, instead of the variables that contain references to these objects (like location)?
The obvious solution would be this, at the end of the method:
scene.internals.draw = location.slice();
This is OK, the only side effect is that I will have to write the original name twice, and edit it twice, which isn't such a big issue. But, I maybe find myself in other situations where I just might need that functionality, so I'd still like an answer.
There is no assignment by reference in javascript, so you cannot do this. What you are doing is usually mistaken for assignment by reference but it is in fact a copy of a reference value which has implications like this.
You probably have a deeper problem somewhere since you are doing this but I don't wanna get into that.
You could do this:
location.splice( 0, location.length ); //Remove all items in the array
location.push.apply( location, buff1.concat(buff2) ); //Push the buffers into the array
To use your term, there are no "real" objects in Javascript - there are only objects, and the variables that hold references to them.
When you assign to location you're just creating an additional reference to an object. The system has no knowledge of which "real" object it was, nor of any other variables that may hold references to it.
So when you reassign to location you're just overwriting that particular reference. Any other original references to the object will stay pointing just where they were.

How come setting value in one array key is setting in another key?

I have an array of objects with various objects within to hold UI values. I wanted to have a button so that element 0's values are replicated through the whole array. However I noticed that setting one set them all. Here is an example without using any looping:
console.log('manual 3: ', lis[3].spacer.divider.type); // prints 'none'
lis[1].spacer.divider.type = 'bananas';
console.log('manual 3: ', lis[3].spacer.divider.type); // prints 'bananas'
I am completely baffled how setting lis[1] also set lis[3]
They must both be references to the same object.
If they're DOM nodes, you can copy them using cloneNode().
Watch out for IE bugs - it has a habit of not cloning properly (for example cloning a <select> doesn't maintain the selectedIndex).
See also What is the most efficent way to clone a JavaScript object? for cloning objects.
Because the variables are reference variables and they all reference the same object and as a result it looks like changing one changes all of them, really they are all the same underlying object.
If you want lots of unique arrays they should all be created as a fresh or be clones of each other.
It turns out I was referencing the same object. Thanks. It didn't click to me since all the other objects above (spacer,lis) were unique. I accidentally was setting divider to a member default of spacer instead of a function returning the default.
Thanks!

Categories

Resources