javascript - 状态不更新

标签 javascript reactjs

我正在 ReactJS 中编写排序可视化工具,并使用状态来保持每次渲染之间的延迟。 当我更改延迟 slider 时,排序不会更新。 我让它记录更新的值,并且在每个循环中我让它记录读取的值。 由于某种原因,当我在循环内部和外部读取 getDelay 时,它们是不同的。

这是代码:

import React, { useState, useEffect } from "react";
import "./SortingVisualizer.css";

class Bar {
    constructor(value, className) {
        this.value = value;
        this.className = className;
    }
}

const SortingVisualizer = () => {
    const [getArray, setArray] = useState([Bar]); //array to hold the bars
    const [getSlider, setSlider] = useState(50);
    const [getDelay, setDelay] = useState(2);
    //reset the array at the start
    useEffect(() => {
        resetArray(10);
    }, []);

    //function to reset the array
    const resetArray = () => {
        const array = [];
        for (let i = 0; i < getSlider; i++) {
            array.push(new Bar(randomInt(20, 800), "array-bar"));
        }
        setArray(array);
    };
    //a delay function. use like this: `await timer(time to wait)`
    const timer = delay => {
        return new Promise(resolve => setTimeout(resolve, delay));
    };

    //function to do buuble sort with given delay between each comparison
    const bubbleSort = async () => {
        let temp,
            array = Object.assign([], getArray); // defining a temporary variable, and a duplicate array the the bars array
        //looping from the array size to zero, in cycles
        for (let i = array.length; i > 0; i--) {
            //looping from the start of the section from the first loop to the end of it.
            for (let j = 0; j < i - 1; j++) {
                //changing the colors of the compared bares
                array[j].className = "array-bar compared-bar";
                array[j + 1].className = "array-bar compared-bar";
                if (getDelay > 0) await timer(getDelay / 2);
                setArray([...array]);
                //comparing and switching if needed
                if (array[j].value > array[j + 1].value) {
                    temp = array[j].value;
                    array[j].value = array[j + 1].value;
                    array[j + 1].value = temp;
                    setArray([...array]);
                }
                //updating the array and moving to the next pair
                if (getDelay > 0) await timer(getDelay / 2);
                array[j].className = "array-bar";
                array[j + 1].className = "array-bar";
                // Wait delay amount in ms before continuing, give browser time to render last update
            }
            array[i - 1].className = "array-bar completed-bar";
        }
        setArray([...array]);
        console.log("done.");
    };

    const combSort = async () => {
        let temp,
            swapped,
            array = Object.assign([], getArray); // defining a temporary variable, and a duplicate array the the bars array
        //looping from the array size to zero, in cycles
        for (let i = array.length; i > 0; i = Math.floor(i / 1.3)) {
            //looping from the start of the section from the first loop to the end of it.
            swapped = false;
            for (let j = 0; j < array.length - i; j++) {
                //changing the colors of the compared bares
                array[j].className = "array-bar compared-bar";
                array[j + i].className = "array-bar compared-bar";
                setArray([...array]);
                await timer(getDelay / 2);
                //comparing and switching if needed
                if (array[j].value > array[j + i].value) {
                    temp = array[j].value;
                    array[j].value = array[j + i].value;
                    array[j + i].value = temp;
                    setArray([...array]);
                    swapped = true;
                    await timer(getDelay / 2);
                }
                //updating the array and moving to the next pair
                array[j].className = "array-bar";
                array[j + i].className = "array-bar";
                // Wait delay amount in ms before continuing, give browser time to render last update
                console.log(getDelay);
            }
            //array[i - 1].className = "array-bar completed-bar";
            if (i === 1 && swapped) i = 2;
        }
        setArray([...array]);
    };

    const sliderUpdate = e => {
        setSlider(e.target.value);
        resetArray(getSlider);
    };
    const delayUpdate = e => {
        setDelay(e.target.value * 1);
        console.log(getDelay);
    };
    return (
        <>
            <div className="menu">
                <button onClick={() => resetArray()}>Geneate new array</button>
                <button onClick={() => bubbleSort()}>Do bubble sort</button>
                <button onClick={() => combSort()}>Do comb sort</button>
            </div>
            <div class="slide-container">
                <input
                    type="range"
                    min="3"
                    max="250"
                    value={getSlider}
                    class="slider"
                    id="sizeSlider"
                    onChange={sliderUpdate}
                />
                <input
                    type="range"
                    min="0"
                    max="1000"
                    value={getDelay}
                    class="slider"
                    id="delaySlider"
                    onChange={delayUpdate}
                />
            </div>
            <div className="array-container">
                {getArray.map((bar, i) => (
                    <div
                        className={getArray[i].className}
                        key={i}
                        style={{ height: `${bar.value * 0.1}vh` }}
                    ></div>
                ))}
            </div>
        </>
    );
};

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

最佳答案

我不知道最好的解决方案是什么,但是解决方案是使用 useRef .

该问题与 Why am I seeing stale props or state inside my function? 有关:在每次渲染中,您都为 bubbleSortcombSort 创建新函数。这些函数使用创建这些函数时存在的 getDelay 值。当点击其中一个按钮时,将执行最后渲染的函数的“版本”,因此将使用当时存在的 getDelay 值。

现在,更改 slider 将导致重新渲染,因此会创建新版本的 bubbleSortcombSort ...但这些不是当前正在运行的版本!

useRef 解决了这个问题,因为我们不是直接引用延迟,而是引用一个其 current 属性存储延迟的对象。对象不会改变,但是 current 属性会改变,并且每次访问它时我们都会获得当前值。我强烈建议您阅读该文档。

在状态变量之后添加

const delayRef = useRef(getDelay);
delayRef.current = getDelay

第二行使引用与状态保持同步。

除了 slider 本身的 value 之外,在其他任何引用 getDelay 的地方,请使用 delayRef.current 代替。例如:

if (delayRef.current > 0) await timer(delayRef.current / 2);

演示(无法使其在 SO 上工作):https://jsfiddle.net/wuf496on/

关于javascript - 状态不更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60831612/

相关文章:

javascript - Chart.js - 绘制具有相反条形的图表 - 如何将 y 轴两端设置为正数?

javascript - Flask 服务器无法读取 POST 请求上传的文件

javascript - React hooks 第一次没有设置状态

javascript - React Component 的 onClick 处理程序未绑定(bind)到 "this"

javascript - 尝试获取表 react js单击的行的数据

javascript - AWS "Cannot GET/"

javascript - 在 Google Gears Workerpool 中加密 JSON

javascript - 函数不响应按钮单击

javascript - 如何在一个列表中隐藏除前两个之外的所有 li,在另一个列表中隐藏第三个和第四个?

javascript - 如何在map函数中获取项目索引