Reputation: 598
I have a simple program that generates an array. I have added the functionality of reversing the array on button click, but when I click on the reverse the displayed array is not reverse but stayed as is. I am using setTimeout
so it can be visualized. Why the visualization is not occuring?
Here is my code:
import React, { useState, useEffect } from 'react';
const Dummy2 = () => {
const [arr, setArr] = useState([]);
const [length, setLength] = useState(10);
useEffect(() => {
generateArray();
}, [length]);
const generateArray = () => {
const temp = [];
for(let i = 0; i < length; i++) {
temp.push(i + 1);
}
setArr(temp);
}
const handleLength = (e) => {
setLength(e.target.value);
}
const reverseArray = () => {
let delay = 1;
for(let i = 0; i < length / 2; i++) {
setTimeout(() => {
const temp = arr;
const t1 = arr[i];
const t2 = arr[length - 1 - i];
temp[i] = t2;
temp[length - 1 - i] = t1;
setArr(temp);
}, delay * 1);
delay++;
}
}
const printArray = () => {
console.log(arr);
}
const maxVal = Math.max(...arr);
return (
<div>
<div className="array-container" style={{height: '50%'}}>
{arr.map((value, idx) => (
<div className="array-element"
key={idx}
style={{height: `${(value * 100 / maxVal).toFixed()}%`,
width: `calc(${100 / length}% - 2px)`,
margin: '0 1px',
display: 'inline-block',
backgroundColor: 'black',
color: 'white'}}
>{value}</div>))
}
</div>
<div>
<button onClick={() => generateArray()}>New array</button>
<button onClick={() => reverseArray()}>Reverse</button>
<button onClick={() => printArray()}>Print array</button>
</div>
<div className="slider-container">
1
<input type="range"
min="1"
max="100"
onChange={(e) => handleLength(e)}
className="slider"
id="myRange"
/>
100
</div>
{length}
</div>
);
}
export default Dummy2;
Upvotes: 0
Views: 326
Reputation: 1759
In reverseArray
function, you are setting state with each iteration, that causing error there.
You need to reverse array completely and then assign it to state.
Change you
reverseArray
function like below:
const reverseArray = () => {
const temp = [...arr];
let delay = 1000;
for (let i = 0; i < length / 2; i++) {
setTimeout(() => {
const t1 = arr[i];
const t2 = arr[length - 1 - i];
temp[i] = t2;
temp[length - 1 - i] = t1;
setArr([...temp]);
}, delay);
delay+=1000;
}
}
Working example:
Inserted timeout
for visualization with 1s
time difference in each iteration.
Upvotes: 1
Reputation: 4987
Here is what I can propose you :
import React, { Component } from "react";
import { render } from "react-dom";
import React, { useState, useEffect } from "react";
const Dummy2 = () => {
const [arr, setArr] = useState([]);
const [length, setLength] = useState(10);
const [reverseIndex, setReverseIndex] = useState(0);
const [didMount, setDidMount] = useState(true);
const [shouldReverseArray, setShouldReverseArray] = useState(true);
useEffect(() => {
generateArray();
}, [length]);
useEffect(() => {
// After clicking on 'reverse' button, reverseIndex is edited. Thus this effect is triggered because it listen to 'reverseIndex'. DidMount is here to prevent triggering the effect when the component is mounted
if (!didMount) {
if (shouldReverseArray && reverseIndex < length / 2) {
setTimeout(() => reverseArray(), 1000);
} else {
shouldReverseArray = setShouldReverseArray(false);
setReverseIndex(0);
}
} else {
setDidMount(false);
}
}, [reverseIndex]);
const generateArray = () => {
const temp = [];
for (let i = 0; i < length; i++) {
temp.push(i + 1);
}
setArr(temp);
};
const handleLength = e => {
setLength(e.target.value);
};
const reverseArray = () => {
const temp = Object.assign([], arr); // Make a copy of your state because it is not mutable
const tempStart = temp[reverseIndex];
const tempEnd = temp[length - tempStart];
temp[reverseIndex] = tempEnd;
temp[length - tempStart] = tempStart;
setArr(temp); // Set the new arr
setReverseIndex(prevState => ++prevState); // Set the new index
};
const printArray = () => {
console.log(arr);
};
const maxVal = Math.max(...arr);
return (
<div>
<div className="array-container" style={{ height: "50%" }}>
{arr.map((value, idx) => (
<div
className="array-element"
key={idx}
style={{
height: `${((value * 100) / maxVal).toFixed()}%`,
width: `calc(${100 / length}% - 2px)`,
margin: "0 1px",
display: "inline-block",
backgroundColor: "black",
color: "white"
}}
>
{value}
</div>
))}
</div>
<div>
<button onClick={() => generateArray()}>New array</button>
<button onClick={() => {setShouldReverseArray(true); reverseArray()}}>Reverse</button>
<button onClick={() => printArray()}>Print array</button>
</div>
<div className="slider-container">
1
<input
type="range"
min="1"
max="100"
onChange={e => handleLength(e)}
className="slider"
id="myRange"
/>
100
</div>
{length}
</div>
);
};
export default Dummy2;
render(<Dummy2 />, document.getElementById("root"));
Here is the Stackblitz repro.
Note that when you change the size of your array, reverse act weirdly, I don't know if it comes from my code or yours, i'll try to figure out.
Also, if you don't need to display each changes, you could use .reverse()
that would be really easier.
[Edit] : Ok the bug come from the fact that reverseIndex is not reseted. I edited the code to add the 'shouldReverseArray' state. I call it in the 'onClick' event in the jsx. Pretty dirty, but i can't think about something else here. Hope it helps tho :)
Upvotes: 1