Reputation: 3
I am trying to implement sorting algorithms and build react app to display how unsorted array is changing with each iteration.
To make it visible, app has to stop for some time after every iteration and I'm trying to do this with setTimeout function and useEffect hook but it doesn't work.
Here is my component:
import { useEffect, useState } from "react"
import { SortAlgorithms } from "../Utils/Sort"
export default function SortingBoard(){
const [board, setBoard] = useState([10,9,8,7,6,5,4,3,2,1])
const [delay, setDelay] = useState(500)
const [algorithm, setAlgorithm] = useState(() => SortAlgorithms[0])
const [isIterating, setIsIterating] = useState(false)
useEffect(() => {
async function sleep(){
const timer = new Promise(r => setTimeout(r, delay));
await timer
}
sleep()
}, [board])
const handleSort = async () => {
setIsIterating(true)
algorithm({board, setBoard})
setIsIterating(false)
}
return (
<div className="bord-layout">
<div className="bord">
{board.map( (item, idx) =>{
return <h1 key={idx}>{item}</h1>
})}
</div>
<div className="bord-options">
<select name="algorithms" onChange={e => setAlgorithm(() => SortAlgorithms[e.target.value])}>
<option value={0}>Bubble Sort</option>
<option value={1}>Sellection Sort</option>
</select>
<button disabled={isIterating} onClick={handleSort}>
Sort
</button>
</div>
</div>
)
}
Here are sort functions:
function BubbleSort({board, setBoard}){
for(let i = 0; i < board.length; i++){
for(let j = 0; j < board.length - 1; j++){
if(board[j] > board[j + 1]){
const temp = board[j]
board[j] = board[j + 1]
board[j + 1] = temp
setBoard([...board])
}
}
}
}
function SelectionSort({board, setBoard}){
for(let i = 0; i < board.length; i++){
let min = i
for(let j = i + 1; j < board.length; j++){
if( board[j] < board[min]){
min = j
}
}
const temp = board[i]
board[i] = board[min]
board[min] = temp
setBoard([...board])
}
}
export const SortAlgorithms = [BubbleSort, SelectionSort]
In particular, it calls setTimeout on every iteration but doesn't wait for them to resolve.
Should i use useEffect and setTimeout in this case or there is other approach I can use?
Upvotes: 0
Views: 1047
Reputation: 16
The problem with your current implementation is that the sort algorithms are synchronous and you are trying to use setTimeout inside the useEffect which really doesn't affect the synchronous execution of the sort algorithms. To fix this you can use setTimeout in the sort algorithms themselves and make them asynchronous.
Your sorting algorithms should look like this:
async function BubbleSort({ board, setBoard }) {
const delay = 500;
const newBoard = [...board];
for (let i = 0; i < newBoard.length; i++) {
for (let j = 0; j < newBoard.length - 1; j++) {
if (newBoard[j] > newBoard[j + 1]) {
const temp = newBoard[j];
newBoard[j] = newBoard[j + 1];
newBoard[j + 1] = temp;
await new Promise((resolve) => setTimeout(resolve, delay));
setBoard([...newBoard]);
}
}
}
}
async function SelectionSort({ board, setBoard }) {
const delay = 500;
const newBoard = [...board];
for (let i = 0; i < newBoard.length; i++) {
let min = i;
for (let j = i + 1; j < newBoard.length; j++) {
if (newBoard[j] < newBoard[min]) {
min = j;
}
}
const temp = newBoard[i];
newBoard[i] = newBoard[min];
newBoard[min] = temp;
await new Promise((resolve) => setTimeout(resolve, delay));
setBoard([...newBoard]);
}
}
You can now remove the useEffect block with the sleep function as it is no longer needed. Also, you need to make handleSort asynchronous and wait for the call to the algorithm:
const handleSort = async () => {
setIsIterating(true);
await algorithm({ board, setBoard });
setIsIterating(false);
};
When making these changes, the sorting algorithms will now pause for the specified delay between each iteration and the component will re-render accordingly.
Upvotes: 0