在此代码中,我希望输出打印“Output:,1,2,3,4,5” 但是输出变量是陈旧的,因为我在从闭包返回承诺后迭代地调用函数。
有什么有效的方法可以让这种定时睡眠效果保持循环,并获得我想要的结果?
应对沙盒:https://codesandbox.io/s/hidden-shape-6uh5jm?file=/src/App.js:0-1092
import "./styles.css";
import React, { useEffect, useState } from "react";
export default function App() {
const [output, setOutput] = useState("Output: ");
const [isLooping, setIsLooping] = useState(false);
const sleep = (ms) => {
return function (x) {
return new Promise((resolve) => setTimeout(() => resolve(x), ms));
};
};
const startLoop = () => {
if (isLooping) {
console.log("Click ignored - because is Looping");
return;
} else {
setIsLooping(true);
runLoop();
}
};
const runLoop = (maxIterations = 0) => {
setOutput(output + ", " + maxIterations);
if (maxIterations >= 5) {
setIsLooping(false);
return;
}
sleep(1000)().then(() => {
runLoop(maxIterations + 1);
});
};
useEffect(() => {
startLoop();
}, []);
return (
<div className="App">
<p>{output}</p>
<button
value="Start Loop"
onClick={() => {
startLoop();
}}
>
Start loop
</button>
{isLooping && <p>Is Looping</p>}
</div>
);
}
任何时候next状态值取决于previous状态值,例如就像一个值与前一个值的字符串连接,你会想要使用一个功能状态更新。
更新
runLoop
处理程序以使用功能状态更新,这确实是一个相当微不足道的更改:setOutput(output + ", " + maxIterations);
到 setOutput(output => output + ", " + maxIterations);
.
const runLoop = (maxIterations = 0) => {
setOutput(output => output + ", " + maxIterations);
if (maxIterations >= 5) {
setIsLooping(false);
return;
}
sleep(1000)().then(() => {
runLoop(maxIterations + 1);
});
};
虽然这会导致奇怪的输出:
"Output: , 0, 1, 2, 3, 4, 5"
这基本上是一个经典的栅栏柱问题.
这是将要呈现的 UI 与要从中呈现它的数据混合到状态的结果。请记住,在 React 中,UI(例如渲染的内容)是状态和道具的函数。 React 状态应该包含 just 您需要存储的数据,React 组件将状态映射到呈现的 UI。
这里是存储just
maxIterations
值并从该状态呈现计算输出字符串的示例。
import "./styles.css";
import React, { useEffect, useState } from "react";
export default function App() {
const [output, setOutput] = useState([]); // <-- initial empty array
const [isLooping, setIsLooping] = useState(false);
const sleep = (ms) => {
return function (x) {
return new Promise((resolve) => setTimeout(() => resolve(x), ms));
};
};
const startLoop = () => {
if (isLooping) {
console.log("Click ignored - because is Looping");
return;
} else {
// reset array for next looping
setOutput([]);
setIsLooping(true);
runLoop();
}
};
const runLoop = (maxIterations = 0) => {
setOutput((output) => output.concat(maxIterations)); // <-- append new value, return new array
if (maxIterations >= 5) {
setIsLooping(false);
return;
}
sleep(1000)().then(() => {
runLoop(maxIterations + 1);
});
};
useEffect(() => {
startLoop();
}, []);
return (
<div className="App">
<p>Output: {output.join(", ")}</p> // <-- compute rendered UI
<button
value="Start Loop"
onClick={() => {
startLoop();
}}
>
Start loop
</button>
{isLooping && <p>Is Looping</p>}
</div>
);
}
渲染输出现在将更符合预期
"Output: 0, 1, 2, 3, 4, 5"