如何用useEffect实现排序迭代延迟展示?React排序可视化问题
排序算法可视化React应用的延迟问题解决
问题描述
我正在实现排序算法并构建React应用,用于展示未排序数组在每轮迭代中的变化。为了让变化可见,应用需要在每次迭代后暂停一段时间,我尝试使用setTimeout函数和useEffect Hook实现,但未生效。
组件代码
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> ) }
排序函数代码
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]
具体问题是:每次迭代都会调用setTimeout,但不会等待其完成。请问我是否应该使用useEffect和setTimeout,还是有其他可行方案?
问题根源
- 同步循环导致批量更新:排序函数是同步for循环,会瞬间执行完所有交换逻辑并调用
setBoard,React会批量处理这些状态更新,最终只渲染最后一次结果,中间步骤完全看不到。 - useEffect的sleep无效:
useEffect里的sleep只是在board变化后等待,但排序过程是同步完成的,等sleep执行时所有迭代已经结束。 - handleSort未等待排序完成:
algorithm是同步函数,调用后立刻执行setIsIterating(false),导致按钮提前解禁。
解决方案:异步排序函数+等待执行
不需要依赖useEffect做延迟,直接把排序函数改成异步,在每次迭代(或交换)后加入延迟,同时让handleSort等待排序完成。
修改后的排序函数代码
async function BubbleSort({ board, setBoard, delay }) { // 复制原数组,避免直接修改状态数组 let currentBoard = [...board]; for (let i = 0; i < currentBoard.length; i++) { for (let j = 0; j < currentBoard.length - 1 - i; j++) { if (currentBoard[j] > currentBoard[j + 1]) { // 交换元素 [currentBoard[j], currentBoard[j + 1]] = [currentBoard[j + 1], currentBoard[j]]; // 更新状态 setBoard([...currentBoard]); // 等待延迟 await new Promise(resolve => setTimeout(resolve, delay)); } } } } async function SelectionSort({ board, setBoard, delay }) { let currentBoard = [...board]; for (let i = 0; i < currentBoard.length; i++) { let min = i; for (let j = i + 1; j < currentBoard.length; j++) { if (currentBoard[j] < currentBoard[min]) { min = j; } } // 交换元素 [currentBoard[i], currentBoard[min]] = [currentBoard[min], currentBoard[i]]; setBoard([...currentBoard]); // 等待延迟 await new Promise(resolve => setTimeout(resolve, delay)); } } export const SortAlgorithms = [BubbleSort, SelectionSort];
修改后的组件代码
import { 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) const handleSort = async () => { setIsIterating(true) // 传入delay参数,等待排序完成 await algorithm({ board, setBoard, delay }) setIsIterating(false) } return ( <div className="bord-layout"> <div className="bord"> {board.map( (item, idx) => ( <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}>Selection Sort</option> </select> <button disabled={isIterating} onClick={handleSort}> Sort </button> </div> </div> ) }
关键改动说明
- 排序函数异步化:给排序函数加上
async关键字,在每次状态更新后用await setTimeout实现延迟,让React有时间渲染中间状态。 - 传递delay参数:把组件中的
delay状态传给排序函数,控制每步的等待时间。 - 复制状态数组:不直接修改原
board数组,而是创建副本currentBoard,避免状态突变导致的不可预测问题。 - 等待排序完成:在
handleSort中用await调用排序函数,确保排序结束后再解禁按钮。 - 移除无用的useEffect:原来的
useEffect对延迟没有帮助,直接删除即可。
内容的提问来源于stack exchange,提问作者Marlboro_Red
相关产品推荐
相关产品推荐

