React中useEffect调用quickSort递归引发无限循环的解决方案咨询
解决快速排序可视化的无限循环问题
你的代码陷入无限循环主要有几个核心问题:
- 直接修改状态数组(
swap和partition操作原数组引用),违反React状态不可变原则,导致状态更新逻辑混乱,触发不必要的重复渲染。 - 在
setArr的同步回调里递归调用quickSort,每次setArr都会触发组件重新渲染,而每次渲染都会重新定义quickSort,递归又会再次触发setArr,形成闭环。 quickSort内的for循环批量创建setTimeout,叠加递归后会持续触发状态更新,彻底停不下来。
修复方案
- 缓存排序函数:用
useCallback包裹所有排序相关函数,避免每次渲染重新创建,消除不必要的依赖触发。 - 遵守状态不可变:所有数组操作都基于原数组的副本,绝不直接修改原状态数组。
- 添加排序锁:用
isSorting状态标记排序状态,防止重复触发排序逻辑。 - 异步递归调度:改用Promise+setTimeout实现异步递归,确保每一步更新完成后再执行下一步,避免任务叠加。
修改后的代码
import { useEffect, useContext, useCallback, useState } from "react"; import { arrContext } from '../arr-context-provider'; import Visualizer from "../visualizer"; const QuickSort: React.FC = () => { const [arr, setArr] = useContext<[number[], React.Dispatch<React.SetStateAction<number[]>>]>(arrContext); const [isSorting, setIsSorting] = useState(false); console.log("Quick Sort"); // 缓存swap函数,返回新数组 const swap = useCallback((arr: number[], i: number, j: number) => { const newArr = [...arr]; [newArr[i], newArr[j]] = [newArr[j], newArr[i]]; return newArr; }, []); // 缓存partition函数,返回分区后的新数组和索引 const partition = useCallback((arr: number[], left: number, right: number) => { let newArr = [...arr]; const pivotValue = newArr[right]; let partitionIndex = left; for (let i = left; i < right; i++) { if (newArr[i] < pivotValue) { newArr = swap(newArr, i, partitionIndex); partitionIndex++; } } newArr = swap(newArr, right, partitionIndex); return { partitionedArr: newArr, pivotIndex: partitionIndex }; }, [swap]); // 缓存quickSort,异步递归避免同步触发无限渲染 const quickSort = useCallback(async (arr: number[], left = 0, right = arr.length - 1) => { if (left >= right || isSorting) return; const { partitionedArr, pivotIndex } = partition(arr, left, right); // 等待当前分区更新完成 await new Promise(resolve => setTimeout(() => { setArr(partitionedArr); resolve(null); }, 100)); // 递归处理左右分区 await quickSort(partitionedArr, left, pivotIndex - 1); await quickSort(partitionedArr, pivotIndex + 1, right); }, [partition, setArr, isSorting]); useEffect(() => { if (!isSorting) { setIsSorting(true); quickSort(arr).finally(() => setIsSorting(false)); } }, [arr, quickSort, isSorting]); return <Visualizer />; }; export default QuickSort;
关键修改说明
- 所有数组操作都返回新数组,彻底避免直接修改原状态导致的状态不一致。
useCallback缓存函数,确保组件渲染时函数引用不变,消除useEffect依赖的不必要触发。isSorting状态作为“锁”,确保同一时间只有一个排序流程在执行,防止重复触发。- 异步递归通过Promise等待每一步更新完成,避免大量
setTimeout任务叠加导致的持续状态更新。
内容的提问来源于stack exchange,提问作者bascott
相关产品推荐
相关产品推荐

