javascript - 状态不更新
问题描述
我在 ReactJS 中编写一个排序可视化器,我使用一个状态来保持每次渲染之间的延迟。当我更改延迟的滑块时,排序不会更新。我让它记录更新的值,并在每个循环中记录它读取的值。出于某种原因,当我阅读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
.
问题与为什么我在函数中看到陈旧的道具或状态有关?bubbleSort
:在每次渲染中,您都在为和创建新函数combSort
。这些函数使用getDelay
创建这些函数时存在的值。当单击其中一个按钮时,将执行最后一次渲染的函数的“版本”,因此getDelay
那个存在的值将被使用。
现在,更改滑块将导致重新渲染,从而创建 和 的新版本bubbleSort
……combSort
但那些不是当前正在运行的版本!
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/
推荐阅读
- python - 如何在 python 中为 antishare(集群化)编写一个 for 循环?
- python - python 中的脚本,用于了解我的日志文件中的错误消息数量
- html - HTML 部分与容器高度不匹配
- python - Scipy 优化器结果不同于 Excel GLG 非线性求解器
- python - 通过类模型的 __init__ 将验证器添加到模型字段会导致错误消息重复
- php - 将 2 个日期与当前日期进行比较,并使其处于活动状态或已过期
- python - 如何使用 REGEX 匹配第二次出现的第一个标记和第二个标记之间的数据?
- python - 我想用单个外键关系创建多个对象
- reactjs - 我无法使用 id 过滤我的数据,出现错误 entry.id is not a function
- git - 为 git fetch 命令设置动态深度