【问题标题】:States not updating状态不更新
【发布时间】:2020-03-24 13:03:15
【问题描述】:

我正在用 ReactJS 编写一个排序可视化器,我使用一个状态来保持每次渲染之间的延迟。 当我更改延迟的滑块时,排序不会更新。 我让它记录更新的值,并且在每个循环中我让它记录 it 读取的值。 出于某种原因,当我在循环内部和外部读取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;

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

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

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

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

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

    在你的状态变量之后,添加

    const delayRef = useRef(getDelay);
    useEffect(() => {delayRef.current = getDelay}, [getDelay]);
    

    第二行,useEffect,保持 ref 与状态同步。

    在您引用getDelay 的其他任何地方,除了滑块本身的value,请改用delayRef.current。例如:

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

    演示(无法在 SO 上运行):https://jsfiddle.net/wuf496on/

    【讨论】:

    • SO sn-p 不支持带有异步的 JS 和所有更新的东西。那是因为 React sn-p 需要 babel,而 SO 有旧版本的 babel。
    猜你喜欢
    • 1970-01-01
    • 2020-11-23
    • 1970-01-01
    • 2018-12-30
    • 2022-01-15
    • 2019-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多