I am building a sorting visualization. I started with a simple bubble sort. When I sort a small array everything is fine and the visualization looks good no matter the speed, but when I visualize a large array there is a delay and the visualization seems to start a few steps ahead and not showing some of the first steps. Why is it happening?
This is my Code:
import React, { useContext, useState, useEffect } from 'react';
const NUMBER_OF_ELEMENTS = 10;
const DEFAULT_COLOR = 'black';
const COMPARE_COLOR = 'darkred';
const DONE_COLOR = 'green';
const SPEED = 150;
const randomIntFromInterval = (min, max) => {
return Math.floor(Math.random() * (max - min + 1) + min);
}
const Dummy = () => {
const [arr, setArr] = useState([]);
const [numberOfElements, setNumberOfElements] = useState(NUMBER_OF_ELEMENTS);
const timeout_id = [];
useEffect(() => {
generateArray();
}, []);
const reset = () => {
resetColors();
generateArray();
}
const generateArray = () => {
const arr1 = [];
for(let i = 0; i < numberOfElements; i++)
{
arr1[i] = randomIntFromInterval(5, 100);
}
console.log(arr1);
setArr(arr1);
}
const resetColors = () => {
const arrayBars = document.getElementsByClassName('array-bar');
for(let i = 0; i < arrayBars.length; i++) {
arrayBars[i].style.backgroundColor = DEFAULT_COLOR;
}
}
const bubbleSort = (arr, n) => {
let i, j, temp, swapped, delay = 1;
for(i = 0; i < n - 1; i++)
{
swapped = false;
for(j = 0; j < n - i - 1; j++)
{
createColor([j, j + 1], COMPARE_COLOR, delay++);
if(arr[j] > arr[j + 1])
{
// swap arr[j] and arr[j+1]
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swapped = true;
createAnimation(j, j + 1, delay++);
}
createColor([j, j + 1], DEFAULT_COLOR, delay++);
}
createColor([n - i - 1], DONE_COLOR, delay++);
// If no two elements were
// swapped by inner loop, then break
if(swapped === false) break;
}
const leftovers = [];
for(let k = 0; k < n - i - 1; k++) {
leftovers.push(k);
}
createColor(leftovers, DONE_COLOR, delay++);
}
const createAnimation = (one, two, delay) => {
const arrayBars = document.getElementsByClassName('array-bar');
const id = setTimeout(() => {
const barOneHeight = arrayBars[one].style.height;
const barTwoHeight = arrayBars[two].style.height;
arrayBars[two].style.height = `${barOneHeight}`;
arrayBars[one].style.height = `${barTwoHeight}`;
}, SPEED * delay);
timeout_id.push(id);
}
const createColor = (indexes, color, delay) => {
const arrayBars = document.getElementsByClassName('array-bar');
const id = setTimeout(() => {
for(let i = 0; i < indexes.length; i++) {
arrayBars[indexes[i]].style.backgroundColor = color;
}
}, SPEED * delay);
timeout_id.push(id);
}
const handleSort = (arr) => {
bubbleSort(arr, arr.length);
}
const handlerRange = (e) => {
setNumberOfElements(e.target.value);
}
const stopTimeOuts =() => {
for(let i = 0; i < timeout_id.length; i++) {
clearTimeout(timeout_id[i]);
}
}
return (
<div>
<div className="array-container">
{arr.map((value, idx) => (
<div className="array-bar"
key={idx}
style={{
backgroundColor: DEFAULT_COLOR,
height: `${value}px`,
width: `${100 / arr.length}%`,
display: 'inline-block',
margin: '0 1px'
}}>
</div>
))}
</div>
<div className="buttons-container">
<button onClick={() => handleSort(arr)}>Sort!</button>
<button onClick={() => reset()}>Reset</button>
<button onClick={() => stopTimeOuts()}>Stop!</button>
</div>
<div className="slider-container">
1
<input type="range"
min="1"
max="100"
onChange={(e) => handlerRange(e)}
className="slider"
id="myRange"
/>
100
</div>
{numberOfElements}
</div>
);
}
export default Dummy;
EDIT: I don't know why but it seems that there are times that the delay occurs and there are times that it isn't. For now I still don't know why and how to handle this.
Almost always when there are performance issues in React it has to do with component being rendered multiple times.
Try to change useEffect like this:
useEffect(() => {
generateArray();
}, [NUMBER_OF_ELEMENTS]);
Related
So, I was trying ato build a minesweeper with typescript and got stuck.
My problem is that my renderBody() function is not updating DOM when i change the boardState.
Can someone give me a light on my error here? I'm sure it's something very simple and I'm being stupid.
import React, { useState } from 'react';
import Cell, { CellDataProps } from '../cell';
import { Container as BoardBody } from './styles';
const Board: React.FC<MapProp> = (props: MapProp) => {
const { rows, columns, bombs } = props;
const initBoardData = renderBoardBody({ rows, columns, bombs });
let [boardState, setBoardState] = useState(initBoardData)
const renderBody = (board: CellDataProps[][]) => {
return board.map((boardRow, i) => boardRow.map((data, j) => <Cell
key={`${(i * rows) + j}`}
data={{
isRevealed: data.isRevealed,
isMine: data.isMine,
isFlag: data.isFlag,
neighbour: data.neighbour
}}
onClick={() => handleCellClick(i,j)}
cMenu={() => { }}
/>))
}
function handleCellClick(row: number, column: number): void {
let newBoardState = boardState;
newBoardState[row][column].isRevealed = true;
setBoardState(boardState)
}
return (
<>
<div className='board-header'>
<h1>Board</h1>
</div>
<BoardBody rows={`${rows}`}>
{renderBody(boardState)}
</BoardBody>
</>
)
}
const renderBoardBody = ({ rows, columns, bombs }: MapProp) => {
let board: CellDataProps[][] = [];
for (let i = 0; i < rows; i++) {
board[i] = [];
for (let j = 0; j < columns; j++) {
board[i][j] = {
isRevealed: false,
isMine: false,
isFlag: false,
neighbour: 0,
}
}
}
const incrementNeighbors = (row: number, column: number) => {
for (let i = Math.max(0, row - 1); i <= Math.min(rows - 1, row + 1); i++) {
for (let j = Math.max(0, column - 1); j <= Math.min(columns - 1, column + 1); j++) {
if (row !== i || column !== j)
board[i][j].neighbour++;
}
}
}
const poulateMines = () => {
for (let i = 0; i < bombs; i++) {
const row = ~~(Math.random() * rows)
const column = ~~(Math.random() * columns)
if (board[row][column].isMine) {
continue;
}
board[row][column].isMine = true;
incrementNeighbors(row, column);
}
}
poulateMines()
return board;
}
Edit: for renderBoardBody() constructor function. :)
handleCellClick is changing state to same before.
function handleCellClick(row: number, column: number): void {
let newBoardState = boardState;
newBoardState[row][column].isRevealed = true;
setBoardState(boardState)
}
I think it should be like
function handleCellClick(row: number, column: number): void {
setBoardState(boardState => {
const newBoardState = [...boardState];
newBoardState[row][column].isRevealed = true;
return newBoardState;
});
}
Really popular project by Clement Mihailescu - https://github.com/clementmihailescu/Pathfinding-Visualizer-Tutorial/blob/master/src/PathfindingVisualizer/PathfindingVisualizer.jsx
He has done this using class component, and I want to use functional components
What I want to happen is when mouseDown occurs I want "isWall" property to be set to true.
I reckon I need to modify the useEffect but I don't know how to go about that.
function PathFindingVisualizer() {
const [Grid, setGrid] = useState([]);
const [MouseIsPressed, setMouseIsPressed] = useState(false);
useEffect(() => {
initializeGrid();
}, []);
const initializeGrid = () => {
const grid = [];
for(let i = 0; i < rows; i++){
const currentRow = [];
for(let j = 0; j < cols; j++){
currentRow.push(createNode(i, j));
}
grid.push(currentRow);
};
setGrid(grid);
};
const createNode = (i, j) => {
return {
i,
j,
isStart: i === START_NODE_ROW && j === START_NODE_COL,
isEnd: i === END_NODE_ROW && j === END_NODE_COL,
distance: Infinity,
isVisited: false,
isWall: false,
previousNode: null,
};
};
const gridWithWallToggle = (grid, i, j) => {
const newGrid = [...grid];
const node = grid[i][j];
const newNode = {
...node,
isWall : !node.isWall
};
console.log(newNode);
newGrid[i][j] = newNode;
return newGrid
}
const handleMouseDown = (i, j) => {
const newGrid = gridWithWallToggle(Grid, i, j);
setGrid(newGrid);
setMouseIsPressed(true);
}
const gridWithNodes = (
<div className="grid">
{Grid.map((row, rowIdx) => {
return (
<div key={rowIdx} className="row">
{row.map((node, nodeIdx) => {
const {i, j, isEnd, isStart, isWall} = node;
return (
<Node
key={nodeIdx}
i={i}
j={j}
isEnd={isEnd}
isStart={isStart}
isWall={isWall}
mouseIsPressed={MouseIsPressed}
onMouseDown={handleMouseDown(i, j)}
></Node>
);
})}
</div>
);
})}
</div>
)
return (
<>
<button onClick = {visualizeDijkstra}>Visualize</button>
<div className="board">
{gridWithNodes}
</div>
</>
)
}
export default PathFindingVisualizer
I am learning react and decided to try create a sorting visualizer. I started with bubble sort and pretty much succeeded creating a basic visualizer. I added the option to change the speed of the sorting and the length of the array that is sorted. I tested it a few times and what I found out is that sometimes when displaying the sorted array some elements are not in place. This can be seen when you have a tall element not in the place it should be (the array itself behind the scene is sorted properly). So something happening to the display elements and I do not know what.
After the sorting is completed and if some of the elements are not in place, if I change the speed suddenly the elements jump back to where they suppose to be. I guess this is because the speed is part of the state and a re-redering is happeing.
What should I do to fix this?
Here is my code:
import React, { useContext, useState, useEffect } from 'react';
const NUMBER_OF_ELEMENTS = 10;
const DEFAULT_COLOR = 'white';
const COMPARE_COLOR = 'darkred';
const DONE_COLOR = 'green';
const SPEED = 4;
const SPEEDS = [1, 5, 10, 25, 50, 100, 150, 200, 250, 300];
const randomIntFromInterval = (min, max) => {
return Math.floor(Math.random() * (max - min + 1) + min);
}
const Dummy = () => {
const [arr, setArr] = useState([]);
const [numberOfElements, setNumberOfElements] = useState(NUMBER_OF_ELEMENTS);
const [speed, setSpeed] = useState(SPEED);
const timeout_id = [];
useEffect(() => {
generateArray();
}, [numberOfElements]);
const reset = () => {
resetColors();
generateArray();
}
const generateArray = () => {
const arr1 = [];
for(let i = 0; i < numberOfElements; i++)
{
arr1[i] = randomIntFromInterval(5, 100);
}
console.log(arr1);
setArr(arr1);
}
const resetColors = () => {
const arrayBars = document.getElementsByClassName('array-bar');
for(let i = 0; i < arrayBars.length; i++) {
arrayBars[i].style.backgroundColor = DEFAULT_COLOR;
}
}
const bubbleSort = (arr, n) => {
let i, j, temp, swapped, delay = 1;
for(i = 0; i < n - 1; i++)
{
swapped = false;
for(j = 0; j < n - i - 1; j++)
{
createColor([j, j + 1], COMPARE_COLOR, delay++);
if(arr[j] > arr[j + 1])
{
// swap arr[j] and arr[j+1]
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swapped = true;
createAnimation(j, j + 1, delay++);
}
createColor([j, j + 1], DEFAULT_COLOR, delay++);
}
createColor([n - i - 1], DONE_COLOR, delay++);
// If no two elements were
// swapped by inner loop, then break
if(swapped === false) break;
}
const leftovers = [];
for(let k = 0; k < n - i - 1; k++) {
leftovers.push(k);
}
createColor(leftovers, DONE_COLOR, delay++);
}
const createAnimation = (one, two, delay) => {
const arrayBars = document.getElementsByClassName('array-bar');
const id = setTimeout(() => {
const barOneHeight = arrayBars[one].style.height;
const barTwoHeight = arrayBars[two].style.height;
arrayBars[two].style.height = `${barOneHeight}`;
arrayBars[one].style.height = `${barTwoHeight}`;
}, SPEEDS[speed - 1] * delay);
timeout_id.push(id);
}
const createColor = (indexes, color, delay) => {
const arrayBars = document.getElementsByClassName('array-bar');
const id = setTimeout(() => {
for(let i = 0; i < indexes.length; i++) {
arrayBars[indexes[i]].style.backgroundColor = color;
}
}, SPEEDS[speed - 1] * delay);
timeout_id.push(id);
}
const handleSort = (arr) => {
bubbleSort(arr, arr.length);
}
const handlerRange = (e) => {
setNumberOfElements(e.target.value);
}
const stopTimeOuts =() => {
for(let i = 0; i < timeout_id.length; i++) {
clearTimeout(timeout_id[i]);
}
}
const handleSpeed = (e) => {
setSpeed(e.target.value);
}
const maxVal = Math.max(...arr);
return (
<div>
<div className="array-container" style={{height: '50%', backgroundColor: 'black'}}>
{arr.map((value, idx) => (
<div className="array-bar"
key={idx}
style={{
backgroundColor: DEFAULT_COLOR,
height: `${(value * 100 / maxVal).toFixed()}%`,
width: `${85 / arr.length}%`,
display: 'inline-block',
margin: '0 1px'
}}>
</div>
))}
</div>
<div className="buttons-container">
<button onClick={() => handleSort(arr)}>Sort!</button>
<button onClick={() => reset()}>Reset</button>
<button onClick={() => stopTimeOuts()}>Stop!</button>
</div>
number of elements: {numberOfElements}
<div className="slider-container">
1
<input type="range"
min="1"
max="100"
onChange={(e) => handlerRange(e)}
className="slider"
id="myRange"
/>
100
</div>
speed: {speed}
<div className="slider-container">
1
<input type="range"
min="1"
max="10"
onChange={(e) => handleSpeed(e)}
className="slider"
id="myRange"
/>
10
</div>
</div>
);
}
export default Dummy;
Set the key inside of the div to be a unique identifier instead of idx - in this case you can use value:
/* -- snip -- */
<div className="array-bar"
key={value}
/* -- snip -- */
This will stop react recycling each div with their respective indexes and instead re-render and re-order based on the new array.
The reason you see the unexpected behaviour is because react uses keys to identify each element. So if you sort your array then apply an index as the id on render, react will get the element that was first rendered with that index as it's key and put it in that place. By changing the key to a unique identifier, react does not get the elements mixed up (since the unique identifier never changes with respect to its element) and can now accurately render each item in the order you intend.
Example:
We render a list:
<div id=1>Foo</div> // Id "1" is now bound to this element
<div id=2>Bar</div>
So if you then reorder the list like this (note the id changes):
<div id=1>Bar</div> // this will be transformed to <div id=1>Foo</div>
<div id=2>Foo</div> // this will be transformed to <div id=2>Bar</div>
Then react will transform the elements into whatever id they were assigned to first - This is why it's important to have unique identifiers. You can generate a unique identifier via a library that generates uuids or other sufficiently random strings.
You can read more here: https://medium.com/#robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318
I am learning react and decided to try create a sorting visualizer. I started with bubble sort and pretty much succeeded creating a basic visualizer. setTimeout was the main function that I used to apply the visualization.
I felt that relaying on setTimeout does not utilize react enough and I wanted to try different approach, applying the visualization with useState hook and the rerendering that is happening when changing the state. I understand that useState hook is asynchronous, and will not immediately reflect.
Here is my code:
import React, { useContext, useState, useEffect } from 'react';
const NUMBER_OF_ELEMENTS = 10;
const DEFAULT_COLOR = 'black';
const randomIntFromInterval = (min, max) => {
return Math.floor(Math.random() * (max - min + 1) + min);
}
const Dummy = () => {
const [arr, setArr] = useState([]);
const [numberOfElements, setNumberOfElements] = useState(NUMBER_OF_ELEMENTS);
const [doneElements, setDoneElements] = useState([]);
useEffect(() => {
resetArray();
}, []);
const resetArray = () => {
const arr1 = [];
for(let i = 0; i < numberOfElements; i++)
{
arr1[i] = randomIntFromInterval(5, 100);
}
console.log(arr1);
setArr(arr1);
}
const bubbleSort = (arr, n) => {
let i, j, temp, swapped, delay = 1;
for(i = 0; i < n - 1; i++)
{
swapped = false;
for(j = 0; j < n - i - 1; j++)
{
createColor(j, j + 1, delay++, 'darkred');
if(arr[j] > arr[j + 1])
{
// swap arr[j] and arr[j+1]
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swapped = true;
createAnimation(j, j + 1, delay++);
}
createColor(j, j + 1, delay++, 'black');
}
createSingleColor(n - i - 1, delay++, 'green');
// If no two elements were
// swapped by inner loop, then break
if(swapped === false) break;
}
for(let k = 0; k < n - i - 1; k++) {
createSingleColor(k, delay++, 'green');
}
}
const createAnimation = (one, two, delay) => {
const arrayBars = document.getElementsByClassName('array-bar');
setTimeout(() => {
const barOneHeight = arrayBars[one].style.height;
const barTwoHeight = arrayBars[two].style.height;
arrayBars[two].style.height = `${barOneHeight}`;
arrayBars[one].style.height = `${barTwoHeight}`;
}, 250 * delay);
}
const createColor = (one, two, delay, color) => {
const arrayBars = document.getElementsByClassName('array-bar');
setTimeout(() => {
arrayBars[two].style.backgroundColor = color;
arrayBars[one].style.backgroundColor = color;
}, 250 * delay);
}
const createSingleColor = (index, delay, color) => {
const arrayBars = document.getElementsByClassName('array-bar');
setTimeout(() => {
arrayBars[index].style.backgroundColor = color;
}, 250 * delay);
}
const handleSort = (arr) => {
bubbleSort(arr, arr.length);
}
const handlerRange = (e) => {
setNumberOfElements(e.target.value);
}
return (
<div>
<div className="array-container">
{arr.map((value, idx) => (
<div className="array-bar"
key={idx}
style={{
backgroundColor: 'black',
height: `${value}px`,
width: `${100 / arr.length}%`,
display: 'inline-block',
margin: '0 1px'
}}>
</div>
))}
</div>
<div className="buttons-container">
<button onClick={() => handleSort(arr)}>Sort!</button>
<button onClick={() => resetArray()}>Reset</button>
<button onClick={() => {
setDoneElements([...doneElements, 7]);
console.log(doneElements);}}>print</button>
</div>
<div className="slider-container">
1
<input type="range"
min="1"
max="100"
onChange={(e) => handlerRange(e)}
className="slider"
id="myRange"
/>
100
</div>
{numberOfElements}
</div>
);
}
export default Dummy;
For example when I tried using the setDoneElements in the bubblesort function I messed up the visualization.
Is there a way to use hooks to apply the visualization, and not to rely on setTimeout that much?
Found a solution:
import React, { useState, useEffect } from 'react';
const shortid = require('shortid');
const randomIntFromInterval = (min, max) => {
return Math.floor(Math.random() * (max - min + 1) + min);
}
const Dummy2 = () => {
const [arr, setArr] = useState([]);
const [length, setLength] = useState(10);
const [doneElements, setDoneElements] = useState([]);
const [compareElements, setCompareElements] = useState([]);
useEffect(() => {
generateArray();
}, [length]);
const generateArray = () => {
setDoneElements([]);
setCompareElements([]);
const tempArr = [];
for(let i = 0; i < length; i++) {
tempArr.push(randomIntFromInterval(7, 107));
}
setArr([...tempArr]);
}
const handleLength = (e) => {
setLength(e.target.value);
}
const bubbleSort = () => {
let i, j, swapped, delay = 100;
const tempArr = [...arr];
const tempDoneElements = [...doneElements];
for(i = 0; i < length - 1; i++)
{
swapped = false;
for(j = 0; j < length - i - 1; j++)
{
createColor([j, j + 1], delay, 'COMPARE');
delay += 100;
if(tempArr[j] > tempArr[j + 1])
{
createAnimation(tempArr, j, j + 1, delay);
delay += 100;
swapped = true;
}
createColor([], delay, 'NONE');
delay += 100;
}
tempDoneElements.push(length - i - 1);
createColor(tempDoneElements, delay, 'DONE');
delay += 100;
// If no two elements were
// swapped by inner loop, then break
if(swapped === false) break;
}
for(let k = 0; k < length - i - 1; k++) {
tempDoneElements.push(k);
}
createColor(tempDoneElements, delay, 'DONE');
delay += 100;
}
const createAnimation = (tempArr, indexOne, indexTwo, delay) => {
const temp = tempArr[indexOne];
tempArr[indexOne] = tempArr[indexTwo];
tempArr[indexTwo] = temp;
const newArr = [...tempArr];
setTimeout(() => {
setArr([...newArr]);
}, delay);
}
const createColor = (tempElements, delay, action) => {
switch(action) {
case 'DONE':
const newDoneElements = [...tempElements];
setTimeout(() => {
setDoneElements([...newDoneElements]);
}, delay);
break;
case 'COMPARE':
setTimeout(() => {
setCompareElements([...tempElements]);
}, delay);
break;
default:
setTimeout(() => {
setCompareElements([]);
}, delay);
}
}
const maxVal = Math.max(...arr);
return (
<div>
<div className="array-container" style={{height: '50%'}}>
{arr.map((value, idx) => (
<div className="array-element"
key={shortid.generate()}
style={{height: `${(value * 100 / maxVal).toFixed()}%`,
width: `calc(${100 / length}% - 2px)`,
margin: '0 1px',
display: 'inline-block',
backgroundColor: compareElements.includes(idx) ? 'darkred' :
doneElements.includes(idx) ? 'green' : 'black',
color: 'white'}}
></div>))
}
</div>
<div>
<button onClick={() => generateArray()}>New array</button>
<button onClick={() => bubbleSort()}>Sort</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;
Instead of messing with the DOM I used state to keep track of changes so react will be charge of changing things.
When manipulating the array in the sorting function we need to remember that arr is part of state therefore it is immutable. any change that we do we need to do on a duplicate and apply the changes at the right time so an animation will occur, that is what I done in the createAnimation function.
To keep track on the colors I added to the state doneElements and compareElements. Every time an element get to it's final position it's index is added to doneElements. At any given time only two elements are compared therefore compareElements will contain only two elements or none.
I'm making Bubble Sort visualizer in React.
I've already done it and it's working, but what i want is that it is responsive so i am making that number of bars would shrink, i am doing it by dividing width and 14, because width of one bar is 8px and margin-right is 6px, but when i click button it wont work until i resize it, when i resize it then it will work
Here is my component SortingVisualization.js
import React, { Component } from "react";
import bubbleSort from "../algorithms/bubbleSort";
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
class SortingVisualizer extends Component {
constructor(props) {
super(props);
this.state = {
list: [],
numberOfBars: 100,
widthOfBars: 8,
width: 0,
height: 0,
};
this.bubbleSortImp = this.bubbleSortImp.bind(this);
this.updateWindowDimensions = this.updateWindowDimensions.bind(this);
this.updateNumberOfBars = this.updateNumberOfBars.bind(this);
this.randomArray = this.randomArray.bind(this);
}
componentDidMount() {
this.updateWindowDimensions();
window.addEventListener("resize", this.updateWindowDimensions);
this.updateNumberOfBars();
window.addEventListener("resize", this.updateNumberOfBars);
this.randomArray();
window.addEventListener("resize", this.randomArray);
}
componentWillUnmount() {
window.removeEventListener("resize", this.updateWindowDimensions);
window.removeEventListener("resize", this.updateNumberOfBars);
window.removeEventListener("resize", this.randomArray);
}
updateWindowDimensions() {
this.setState({ width: window.innerWidth, height: window.innerHeight });
}
updateNumberOfBars() {
let num = Math.floor(this.state.width / 15);
this.setState({ numberOfBars: num });
}
randomArray() {
let arr = [];
for (let i = 0; i < this.state.numberOfBars; i++) {
arr.push(this.randomNumber());
}
this.setState({ list: [...arr] });
}
randomNumber() {
let randomIndex = Math.floor(Math.random() * 100) + 1;
return randomIndex;
}
async bubbleSortImp(e) {
e.preventDefault();
let arr = this.state.list;
let len = this.state.list.length;
for (let i = 0; i < this.state.numberOfBars; i++) {
await sleep(50);
bubbleSort(arr, 0, len - 1);
this.setState({ list: [...arr] });
}
}
render() {
console.log(this.state.numberOfBars);
console.log(this.state.width);
return (
<>
<div className="sortingVisualizer">
{this.state.list.map((number, index) => (
<div
key={index}
style={{
height: `${number}` * 5,
width: this.state.widthOfBars,
}}
className="visualize"
></div>
))}
</div>
<div className="buttons">
<button className="btn" onClick={this.bubbleSortImp}>
Bubble Sort
</button>
</div>
</>
);
}
}
export default SortingVisualizer;
Here is my bubbleSort.js
let i = 0;
let j = 0;
const bubbleSort = (arr) => {
if (i < arr.length) {
for (let j = 0; j < arr.length - i - 1; j++) {
let a = arr[j];
let b = arr[j + 1];
if (a > b) {
swap(arr, j, j + 1);
}
}
}
i++;
};
const swap = (arr, a, b) => {
let temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
};
export default bubbleSort;
Here is video what is happening
https://streamable.com/0kk5yt
Here is GitHub Repo
https://github.com/sk0le/sorting-visualization
I found few issues in your implementation and modified the code accordingly. Please refer the below component code and the helper bubbleSort code as well. Hope this helps in resolving your issue.
Initially the sorting was not working because in componentDidMount you were setting the numberOfBars using the computed width but by that time the width didn't get updated in the state, hence the default value is 0. This is making numberOfBars to 0.
And in the utility method, sorting is not happening once the i >= array.lenth as the value is not reset to 0. Found these issues and updated the code accordingly.
Below is the updated code for SortingVisualizer,
//SortingVisualizer.js
import React, { Component } from "react";
import bubbleSort from "../algorithms/bubbleSort";
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
class SortingVisualizer extends Component {
constructor(props) {
super(props);
this.state = {
list: [],
numberOfBars: 100,
widthOfBars: 8,
width: window.innerWidth,
height: window.innerHeight
};
this.bubbleSortImp = this.bubbleSortImp.bind(this);
this.updateWindowDimensions = this.updateWindowDimensions.bind(this);
this.randomArray = this.randomArray.bind(this);
}
getDerivedStateFromProps = (nextProps, prevState) => {
if (
prevState.width !== window.innerWidth ||
prevState.height !== window.innerHeight
) {
return {
width: window.innerWidth,
height: window.innerHeight
};
}
};
componentDidMount() {
this.updateWindowDimensions();
window.addEventListener("resize", this.updateWindowDimensions);
this.randomArray();
window.addEventListener("resize", this.randomArray);
}
componentWillUnmount() {
window.removeEventListener("resize", this.updateWindowDimensions);
window.removeEventListener("resize", this.randomArray);
}
updateWindowDimensions() {
const width = window.innerWidth;
let num = Math.floor(width / 15);
this.setState(() => ({
width,
height: window.innerHeight,
numberOfBars: num
}));
}
randomArray() {
let arr = [];
for (let i = 0; i < this.state.numberOfBars; i++) {
arr.push(this.randomNumber());
}
this.setState(() => ({ list: [...arr] }));
}
randomNumber() {
let randomIndex = Math.floor(Math.random() * 100) + 1;
return randomIndex;
}
async bubbleSortImp(e) {
e.preventDefault();
let arr = this.state.list;
let len = this.state.list.length;
for (let i = 0; i < this.state.numberOfBars; i++) {
await sleep(50);
bubbleSort(arr, 0, len - 1);
this.setState(() => ({ list: [...arr] }));
}
}
render() {
console.log(this.state.numberOfBars);
console.log(this.state.width);
return (
<>
<div className="sortingVisualizer">
{this.state.list.map((number, index) => (
<div
key={index}
style={{
height: `${number}` * 5,
width: this.state.widthOfBars
}}
className="visualize"
></div>
))}
</div>
<div className="buttons">
<button className="btn" onClick={this.bubbleSortImp}>
Bubble Sort
</button>
</div>
</>
);
}
}
export default SortingVisualizer;
Below is the updated code for the utility bubbleSort
let i = 0;
let j = 0;
const bubbleSort = arr => {
if (i < arr.length) {
for (let j = 0; j < arr.length - i - 1; j++) {
let a = arr[j];
let b = arr[j + 1];
if (a > b) {
swap(arr, j, j + 1);
}
}
} else {
i = 0;
return;
}
i++;
};
const swap = (arr, a, b) => {
let temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
};
export default bubbleSort;