Reactjs 直到 for 循环结束才渲染

lav*_*iRZ 4 javascript reactjs

重要的!这不是异步 API 的问题!我尝试创建一个冒泡排序可视化器,当我运行算法本身时,我希望用户实际看到它的运行情况。所以每次我进行交换时,我都希望用户能够看到它发生。当bubbleSort运行内部的循环并更新状态Classes和 时Array,没有任何反应,直到循环完全结束。如果我将 abreak;放入循环中,则在循环停止时反应渲染。问题是什么?我该如何解决?

编辑:

如果你有答案,请解释它为什么起作用,以及我应该如何在我的代码中实现它。

import React, { useState, useEffect } from "react";
import "./SortingVisualizer.css";
import { render } from "@testing-library/react";
const SortingVisualizer = () => {
    const [getArray, setArray] = useState([]);
    const [getClasses, setClasses] = useState([""]);
    useEffect(() => {
        resetArray(200);
    }, []);
    useEffect(() => {}, [getArray, getClasses]);

    const resetArray = size => {
        const array = [];
        const classesArray = [];
        for (let i = 0; i < size; i++) {
            array.push(randomInt(20, 800));
            classesArray.push("array-bar");
        }
        setArray(array);
        setClasses(classesArray);
    };

    const bubbleSort = delay => {
        let temp,
            array = Object.assign([], getArray),
            classes = Object.assign([], getClasses);
        for (let i = array.length; i > 0; i--) {
            for (let j = 0; j < i - 1; j++) {
                classes[j] = "array-bar compared-bar";
                classes[j + 1] = "array-bar compared-bar";
                if (array[j] > array[j + 1]) {
                    temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
                //console.log((array.length - i) * 200 + (j + 1));
                setArray(array);
                setClasses(classes);
                break;
            }
        }
        console.log("done.");
    };
    return (
        <>
            <div className="menu">
                <button onClick={() => resetArray(200)}>Geneate new array</button>
                <button onClick={bubbleSort}>Do bubble sort</button>
            </div>
            <div className="array-container">
                {getArray.map((value, i) => (
                    <div
                        className={getClasses[i]}
                        key={i}
                        style={{ height: `${value * 0.1}vh` }}
                    ></div>
                ))}
            </div>
        </>
    );
};

function randomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
export default SortingVisualizer;
Run Code Online (Sandbox Code Playgroud)

Spi*_*idy 6

React 会尽可能地专注于性能。例如,它会聚合一堆 setState 调用并分批处理它们以防止不必要的渲染。适用于大多数 Web 应用程序,不适用于此类可视化。

您可以做的是等待渲染完成,然后再进入下一个状态。计时器和 async/await 将使这很容易阅读和完成。

// Returns a promise that resolves after given delay
const timer = (delay) => {
    return new Promise((resolve) => setTimeout(resolve, delay));
}

const bubbleSort = async(delay) => {
    let temp,
        array = Object.assign([], getArray),
        classes = Object.assign([], getClasses);
    for (let i = array.length; i > 0; i--) {
        for (let j = 0; j < i - 1; j++) {
            classes[j] = "array-bar compared-bar";
            classes[j + 1] = "array-bar compared-bar";
            if (array[j] > array[j + 1]) {
                temp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            }
            //console.log((array.length - i) * 200 + (j + 1));
            setArray(array);
            setClasses(classes);

            // Wait delay amount in ms before continuing, give browser time to render last update
            await timer(delay);
        }
    }
    console.log("done.");
};
Run Code Online (Sandbox Code Playgroud)

更新

需要更改的下一部分代码是如何修改数组。当您调用 时setArray,React 会查看您传入的值并进行浅层比较以查看它是否与现有值不同。如果它们相同,它会忽略它并且不会重新渲染。对于像 String、Boolean 和 Number 这样的原始数据类型,这不是问题。对于像对象、数组和函数这样的复杂数据类型,这是一个问题,因为它比较的是内存地址,而不是内容。

您的代码正在修改相同的数组,然后将其传递给setArray,后者会看到它已经拥有的相同内存地址并忽略更新。

那么为什么它仍然更新前两个呢?实际上我不太确定。可能点击甚至告诉 React 它应该重新渲染。

以下是解决此问题的方法。确保setArray每次都提供一个新的内存地址,以便触发重新渲染。您的另一个选择是使用强制渲染技术。

// Create a new array and spread the old arrays contents out inside of it.
// New memory address, same contents
setArray([...array]);
Run Code Online (Sandbox Code Playgroud)