You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中useEffect调用quickSort递归引发无限循环的解决方案咨询

解决快速排序可视化的无限循环问题

你的代码陷入无限循环主要有几个核心问题:

  • 直接修改状态数组(swap和partition操作原数组引用),违反React状态不可变原则,导致状态更新逻辑混乱,触发不必要的重复渲染。
  • 在setArr的同步回调里递归调用quickSort,每次setArr都会触发组件重新渲染,而每次渲染都会重新定义quickSort,递归又会再次触发setArr,形成闭环。
  • quickSort内的for循环批量创建setTimeout,叠加递归后会持续触发状态更新,彻底停不下来。

修复方案

  1. 缓存排序函数:用useCallback包裹所有排序相关函数,避免每次渲染重新创建,消除不必要的依赖触发。
  2. 遵守状态不可变:所有数组操作都基于原数组的副本,绝不直接修改原状态数组。
  3. 添加排序锁:用isSorting状态标记排序状态,防止重复触发排序逻辑。
  4. 异步递归调度:改用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 19:54:54