CSS transition lost when array changes - javascript

I have a simple react app that shifts and unshifts an array of letters. The transition animation occurs when a user hits next and back button. Functionally, the array is properly changed, but the transition only works for next. I have a hunch that this may be an issue more basic than React, but I did make sure that the key is unique to prevent redraw.
// this is the issue.
clickLeftRightHandler = () => {
const { list } = this.state;
// Does using slice or shifting the array cause a new redraw? Is it
the CSS?
const newList = [list[list.length-1], ...list.slice(0, -1)];
this.setState({list : newList});
}
Code Link:
https://stackblitz.com/edit/react-7nsrjg
Any help is appreciated!

Just use the unshift method:
clickLeftRightHandler = () => {
const { list } = this.state;
const newList = list.slice(0, -1);
newList.unshift(list[list.length-1]); // <-- right here
this.setState({list : newList});
}
working example
edit
I honsetly don't know why it works, but it seems like if the string is a bit longer, the animation works as you want it to, so this works:
newList.unshift(list[list.length-1]+' ');
example. I don't know why's that happening, truly.

It turns out that the reason the animation wasn't working properly was due to the key being supplied to the Alphabet Component. The solution adds an index state that makes sure the shifted key receives a new key that's different from the cycled key.
It should also be noted that, #yuvi's updated example also implicitly fixes the issue with a new string that'll be passed to the key causing it to be uniquely set.
See updated example

Related

A problematic function for my chess app I built in react.js

I was hoping I can get some tips with a certain function in my chess app. The function tracks if the path for the selected piece is clear or not. The problem I have is that it doesn't seem to quite work. An example would be the rook being able to kill the other rook on the first move of the game, completely ignoring every other piece in it's path. I tried using the reduce method, but to no avail.
Here is my code for the function:
const isPathClean = (srcToDestPath, squares) => {
return srcToDestPath.reduce((acc, curr) => acc && !squares[curr], true);
}
Thanks in advance!
Sounds like you're looking for something like
const isPathClean = (srcToDestPath, squares) => {
// `.some` returns true if the predicate is true
// for any member of the array, i.e. if any of the
// squares on the path is taken.
// The outer `!` negates that, so this returns true
// if the path is clear.
return !srcToDestPath.some(p => squares[p]);
}
but it's really rather hard to tell based on your question.

Can the useState() hook's passed function re-render their component multiple times inside a function?

I've tried searching for this problem but the searches have come up completely empty. I'm relatively new to the JS frameworks. The project I'm working on now is a sorting algorithm visualizer with ReactJS which is supposed to demonstrate how different sorts work step by step. However, I'm having a problem with re-rendering the displayed array every time I call the my setArray() function (the one I used in my useState hook) inside the sorting function.
There's also a recorded video of the problem in the end.
I'm using the <div>'s heights to do the sorting. Here is the bubble sort implementation:
// The hook's definition
const [ array, setArray ] = useState(generateArray(rangeVal));
//---
// Code inbetween
//---
const bubble_Sort = () => {
let list = document.getElementsByClassName('array-container')[0];
let realList = list.children;
let swapp;
let n = realList.length-1;
let x = [...array];
do {
swapp = false;
for (let i=0; i < n; i++)
{
let a = x[i].props.style.height;
let b = x[i+1].props.style.height;
if (a > b)
{
console.log('inside');
let temp = x[i];
x[i] = x[i+1];
x[i+1] = temp;
swapp = true;
// This, I think, is the problematic part
newArray(x); // Shown below
sleep(10); // A custom sleep function I wrote that basically stalls the code
}
}
n--;
} while (swapp);
}
And this is the newArray() method that's supposed to update the array shown on screen every time it's called
const newArray = (newArray) => {
setArray([...newArray]);
}
I'd imagine that every time 2 values get swapped in the sort, the updated array would be shown on the screen through newArray() method since calling my hook's setArray() function should re-render the whole component.
However, what happens is that the whole component remains frozen until the entire array is sorted and only gets shown in its sorted form after the sorting has ended.
I have no idea why this is happening. I'm assuming this has something to do with multiple setArray() calls not working inside a single function, but even if it's so, I can't come up with a solution. Your help would greatly be appreciated. Thanks in advance.
I recorded a video in case seeing the execution helps:
https://streamable.com/hbjk2z
Edit:
The sleep function: https://i.ibb.co/p2CypFK/image-2020-11-13-015800.png

Passing arguments from method to method doesn't seem to change the agrument in typescript

I'm working on a card game in typeScript.
The game does somthing like this:
game = () => {
let get_start_card = () => {
let card:string
// get random card ...
return card
}
let start_round = (player:Player,card:string) => {
console.log(`card at start is: ${card}`)
// some logic ...
// change the card - generate new_card
this.start_card = new_card
}
let game_status: boolean = true
let start_card = get_start_card()
while(game_status === true){
start_round(player1,start_card)
start_round(player2,start_card)
}
}
game()
The idea is that when the next turn starts, the next player start with the card that the other player left at the end of his turn.
The problem is that the start_round method allways start with the original card, and doesn't seem to change, when I write this.start_card or this.this.start_card or anything else.
Does anyone have an idea how to do this?
I the way I try to change the value of start_card before the next round using the keyword "this" is wrong?
What is the correct way to do such thing, OOP-wise?
Edit: removing "this" results in an error, not recognizing "start_card".
Thanks
It should be without this.
start_card = new_card
The OOP way would be to use a class, then you would use this a lot. You've made a closure instead, and start_card is a regular variable, not a property of anything.

Honestly hard to explain.. I use something like a=b, then a++, but b changes. Except, these are Arrays

Well my short and easy to explain explanation can be this. I have 2 arrays, FilterList and GamesReset. Whenever this function I have works and filters out some games with check boxes and a drop down menu, the function starts off with something like FilterList=GamesReset;. This functions seems to work fine until I filter out ages for the game. The function never touches GamesReset unless it's something like while(i<GamesReset.length){} or FilterList=GamesReset;. And the only tool I use when I filter games is FilterList.splice(i,1);. Now with that, GamesReset definitely, should never change as far as I know. I have it to reset FilterList, then depending on what needs to be filtered out, it will start removing those games from the FilterList. The problem I have, is that, GamesReset also becomes filtered. Which, does not make any sense at all. So like my title, it's just like saying b=0;, a=b;, a++;, and now b equals 1.
Now, I think that's the best/shortest way I can reveal this problem, without overdoing it with my bad habit of explaining things to people. I have a webpage currently available if anyone would like to see whats going on in action, because I wouldn't get what's going on with GamesReset either if I were you, here (url removed, read edit). To get the error working, just change the age to 10 without checking any boxes. The bottom paragraph is the GamesReset array (using <br> to separate each array), and it's the one that changes when I'm only changing FilterList in the JavaScript. The actual codes if you view the page source may be a little off compared to when I mentioned above, but it's pretty much 100% the same thing. I also wanted to have the codes available without a url and on this page, but I can't figure out how to do that with the html tags included.
Actually, here's the JavaScript function. I just figured out the 4 spaces thing when my question was rejected.
function SearchFilter() {
Games = GamesReset;
plat = document.getElementById('platformcheck').checked;
rpg = document.getElementById('rpgcheck').checked;
puzz = document.getElementById('puzzlecheck').checked;
hybo = document.getElementById('hybocollectcheck').checked;
ages = document.getElementById('agescheck').value;
if ((!plat) && (!rpg) && (!puzz) && (!hybo)) {
FilterList = Games;
} else {
FilterList = [];
i = 0;
while (i < Games.length) {
Set = '';
Set = Games[i];
Set = Set.split('</>');
StrFind = Set[0];
if (
(plat && (StrFind.search(',platform,') > -1)) || (rpg && (StrFind.search(',rpg,') > -1)) || (puzz && (StrFind.search(',puzzle,') > -1)) || (hybo && (StrFind.search(',hybocollect,') > -1))) {
FilterList.push(Games[i]);
}
i++;
}
// so by now, we should have the filtered array
}
//seperate filter for ages
i = 0;
while (i < FilterList.length) { //The problem should definitely start here
Set = '';
Set = FilterList[i];
Set = Set.split('</>');
StrFind = Set[1];
if ((Math.abs(StrFind)) > ages) {
FilterList.splice(i, 1);
} else {
i++;
}
}
GL.innerHTML = GamesReset.join('<br>');
}
As a reminder, the problem starts when the age filter is working. And the only thing it does is FilterList.splice(i,1);. But it ends up changing GamesReset. I changed this function a bit when I added Games=GamesReset;, but that was another test to try and make sure GamesReset doesn't get filtered like FilterList, but it still does.
EDIT: I removed my url since the answers definitely explained everything, so there's no need for it now.
Arrays are not copied when assigned, both variables will refer to the same data. Here is a post that goes into detail on this: Copying array by value in JavaScript
It makes perfect sense since variables are just references to objects in memory. One object can have several references. Consider this:
var a = { foo: 'bar' };
var b = a;
// b is now a reference to a and they both point to the same object
b.foo = 'doe';
alert( a.foo ); // alerts doe
The same goes for arrays. So when you do FilterList = GamesReset you are not copying the array - you are just assigning the same array to another variable. Any mutations or changes made to either reference will be reflected in all references.
To create a copy of an array you can use slice:
FilterList = GamesReset.slice();

Removing items from data bound array

How do I remove an items from a data bound array? My code follows.
for(var i = 0; i < listBox.selectedIndices.length; i++) {
var toRemove = listFiles.selectedIndices[i];
dataArray.splice(toRemove, 1);
}
Thanks in advance!
Edit Here is my swf. The Add Photos works except when you remove items.
http://www.3rdshooter.com/Content/Flash/PhotoUploader.html
Add 3 photos different.
Remove 2nd photo.
Add a different photo.
SWF adds the 2nd photo to the end.
Any ideas on why it would be doing this?
Edit 2 Here is my code
private function OnSelectFileRefList(e:Event):void
{
Alert.show('addstart:' + arrayQueue.length);
for each (var f:FileReference in fileRefList.fileList)
{
var lid:ListItemData = new ListItemData();
lid.fileRef = f;
arrayQueue[arrayQueue.length]=lid;
}
Alert.show('addcomplete:' + arrayQueue.length);
listFiles.executeBindings();
Alert.show(ListItemData(arrayQueue[arrayQueue.length-1]).fileRef.name);
PushStatus('Added ' + fileRefList.fileList.length.toString() + ' photo(s) to queue!');
fileRefList.fileList.length = 0;
buttonUpload.enabled = (arrayQueue.length > 0);
}
private function OnButtonRemoveClicked(e:Event):void
{
for(var i:Number = 0; i < listFiles.selectedIndices.length; i++) {
var toRemove:Number = listFiles.selectedIndices[i];
//Alert.show(toRemove.toString());
arrayQueue.splice(toRemove, 1);
}
listFiles.executeBindings();
Alert.show('removecomplete:' + arrayQueue.length);
PushStatus('Removed photos from queue.');
buttonRemove.enabled = (listFiles.selectedItems.length > 0);
buttonUpload.enabled = (arrayQueue.length > 0);
}
It would definitely be helpful to know two things:
Which version of ActionScript are you targeting?
Judging from the behavior of your application, the error isn't occurring when the user removes an item from the list of files to upload. Looks more like an issue with your logic when a user adds a new item to the list. Any chance you could post that code as well?
UPDATE:
Instead of: arrayQueue[arrayQueue.length]=lid
Try: arrayQueue.push(lid)
That will add a new item to the end of the array and push the item in to that spot.
UPDATE 2:
Ok, did a little more digging. Turns out that the fileList doesn't get cleared every time the dialog is opened (if you're not creating a new instance of the FileReferenceList each time the user selects new files). You need to call splice() on the fileList after you add each file to your Array.
Try something like this in your AddFile() method...
for(var j:int=0; j < fileRefList.fileList.length; j++)
{
arrayQueue.push(fileRefList.fileList[j]);
fileRefList.fileList.splice(j, 1);
}
That will keep the fileList up to date rather than holding on to previous selections.
I see one issue. The selected indices are no longer valid once you have spliced out the first element from the array. But that should only be a problem when removing multiple items at once.
I think we need to see more code about how you are handling the upload before we can figure out what is going on. It looks to me like you are holding a reference to the removed FileReference or something. The described problem is occurring when you upload a new file, not when you remove the selected one.
Do you mean to use listBox and listFiles to refer to the same thing?
I'm stepping out on a limb here, because I don't have a ton of experience with JavaScript, but I'd do this the same way that I'd do it in C, C++, or Java: By copying the remaining array elements down into their new locations.
Assuming that listFiles.selectedIndices is sorted (and its contents are valid indices for dataArray), the code would be something like the following:
(WARNING: untested code follows.)
// Don't bother copying any elements below the first selected element.
var writeIndex = listFiles.selectedIndices[0];
var readIndex = listFiles.selectedIndices[0] + 1;
var selectionIndex = 1;
while(writeIndex < (dataArray.length - listFiles.selectedIndices.length)) {
if (selectionIndex < listFiles.selectedIndices.length) {
// If the read pointer is currently at a selected element,
// then bump it up until it's past selected range.
while(selectionIndex < listFiles.selectedIndices.length &&
readIndex == listFiles.selectedIndices[selectionIndex]) {
selectionIndex++;
readIndex++;
}
}
dataArray[writeIndex++] = dataArray[readIndex++];
}
// Remove the tail of the dataArray
if (writeIndex < dataArray.length) {
dataArray.splice(writeIndex, dataArray.length - writeIndex);
}
EDIT 2009/04/04: Your Remove algorithm still suffers from the flaw that as you remove items in listFiles.selectedIndices, you break the correspondence between the indices in arrayQueue and those in listFiles.selectedIndices.
To see this, try adding 3 files, then doing "Select All" and then hit Remove. It will start by removing the 1st file in the list (index 0). Now what had been the 2nd and 3rd files in the list are at indices 0 and 1. The next value taken from listFiles.selectedIndices is 1 -- but now, what had been the 3rd file is at index 1. So the former File #3 gets spliced out of the array, leaving the former 2nd file un-removed and at index 0. (Using more files, you'll see that this implementation only removes every other file in the array.)
This is why my JavaScript code (above) uses a readIndex and a writeIndex to copy the entries in the array, skipping the readIndex over the indices that are to be deleted. This algorithm avoids the problem of losing correspondence between the array indices. (It does need to be coded carefully to guard against various edge conditions.) I tried some JavaScript code similar to what I wrote above; it worked for me.
I suspect that the problem in your original test case (removing the 2nd file, then adding another) is analogous. Since you've only shown part of your code, I can't tell whether the array indices and the data in listFiles.selectedIndices, arrayQueue, and fileRefList.fileList are always going to match up appropriately. (But I suspect that the problem is that they don't.)
BTW, even if you fix the problem with using splice() by adjusting the array index values appropriately, it's still an O(N2) algorithm in the general case. The array copy algorithm is O(N).
I'd really need to see the whole class to provide a difinitive answer, but I would write a method to handle removing multiple objects from the dataProvider and perhaps assigning a new array as the dataProvider for the list instead of toying with binding and using the same list for the duration. Like I said, this is probably inefficient, and would require a look at the context of the question, but that is what I would do 9unless you have a big need for binding in this circumstance)
/**
* Returns a new Array with the selected objects removed
*/
private function removeSelected(selectedItems:Array):Array
{
var returnArray:Array = []
for each(var object:Object in this.arrayQueue)
{
if( selectedItems.indexOf(object)==-1 )
returnArray.push( object )
}
return returnArray;
}
You might be interested in this blog entry about the fact that robust iterators are missing in the Java language.
The programming language, you mentioned Javascript, is not the issue, it's the concept of robust iterators that I wanted to point out (the paper actually is about C++ as the programming language).
The [research document]() about providing robust iterators for the ET++ C++ framework may still e helpful in solving your problem. I am sure the document can provide you with the necessary ideas how to approach your problem.

Categories