javascript - 如何在反应中使用循环内的settimeout函数更新状态?
问题描述
我正在创建反应应用程序来可视化排序算法,我停止了这个问题。我正在从 bar 状态循环遍历数组的所有元素,并且我想交换它们(出于测试目的)。这并不像我想要的那样完美,因为它“忽略”了setTimeout
功能并立即执行。我用 setBars 尝试了一些东西,但它也不起作用。我怎样才能做到这一点,以便在setTimeout
函数中设置超时后发生交换?
const [bars, setBars] = useState([23, 63, 236, 17, 2]);
const swap = (arr, i, j) => {
let temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
};
for (let i = 0; i < bars.length - 1; i++) {
setTimeout(() => {
swap(bars, i, i + 1);
}, 2000);
}
解决方案
您希望useEffect
在组件安装时使用挂钩来启动该过程:
useEffect(() => {
// ...code here...
}, []);
最后的空依赖数组表示“仅在组件第一次安装时运行它”。
在其中,您的代码将安排计时器回调:
for (let i = 0; i < bars.length - 1; i++) {
setTimeout(() => {
setBars(bars => {
// Copy the current array
const newBars = [...bars];
// Do the swap
swap(newBars, i, i + 1);
// Set the state by returning the update
return newBars;
});
}, 2000 * (i + 1));
}
请注意,它使用 的回调形式setBars
,以便您在当前状态下操作(而不是bars
,它只是第一次挂载期间使用的数组,因为您的函数仅在第一次挂载发生时被调用)。
另请注意,间隔2000 * (i + 1)
不仅仅是2000
. 所以每个回调都在最后一个回调之后 2000 毫秒发生,因为它们都是同时安排的。
上面的另一个重要方面是它let
在 中使用for
,因此每个循环体都有自己的 i
变量。(如果使用的代码var
,或let
在 之外for
,所有回调将共享同一个i
变量,该变量的值为bars.length
。)
或者,我想我可能会采取这种方法:
由于您想同时更新两者i
,并且bars
最好在异步进行更新时使用状态设置器的回调形式(如setTimeout
),我将合并i
为bars
一个状态项(而我通常会保留它们分开):
const [barState, setBarState] = useState({i: 0, bars: [23, 63, 236, 17, 2]});
然后,当您的组件安装时,您将使用useEffect
钩子启动该过程,然后在每次更改为barState
:
useEffect(() => {
// ...code here...
}, [barState]);
包含在末尾的依赖项数组barState
说“在组件安装和每次barState
更改时运行它”。
在其中,您的代码将安排计时器回调:
if (barState.i < barState.bars.length - 1) {
setTimeout(() => {
setBarState(({i, bars}) => {
// Copy the current array
bars = [...bars];
// Do the swap
swap(bars, i, i + 1);
// Set the state by returning the update
++i;
return {i, bars};
});
}, 2000);
}
现场示例:
const { useState, useEffect } = React;
const swap = (arr, i, j) => {
let temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
};
function Example() {
const [barState, setBarState] = useState({i: 0, bars: [23, 63, 236, 17, 2]});
useEffect(() => {
if (barState.i < barState.bars.length - 1) {
setTimeout(() => {
setBarState(({i, bars}) => {
// Copy the current array
bars = [...bars];
// Do the swap
swap(bars, i, i + 1);
// Set the state by returning the update
++i;
return {i, bars};
});
}, 2000);
}
}, [barState]);
return <div>{barState.bars.join(", ")}</div>;
}
ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
推荐阅读
- javascript - LeetCode 125:回文数 简易 LeetCode
- javascript - 我希望三个圆圈重叠。(使用高图表)
- java - 可以采用不同类型值的 JSON 对象的通用 POJO
- android - Android Room:有没有办法返回查询成功或空的变量?
- typescript - 我怎样才能做一个部分的类型
但传递的引用仅包括来自 Foo 的键 - java - 如何在处理(Java)中添加标题和图例并创建不同的颜色
- reactjs - 为什么 useEffect() 在从套接字接收数据后似乎重置了我的本地状态?
- xslt - 如何使用 XSLT 转换删除循环元素,其中某些标记没有值
- arrays - MongoDB 使用来自另一个集合的查询结果更新集合中的所有记录
- csv - 使用 csv 库排序,错误提示我的日期与 '%Y-%m-%d' 格式不匹配