代码和更多说明-此处为
我正在开发一个计算量很大的网络应用程序,我想让用户知道计算的进展,这样应用程序就不会显得过时。基本上,我有三条消息要在计算状态下指示——
- 加载
- 50%已完成
- 80%已完成
我在App
组件中创建了一个displayMsg
状态,该状态作为道具发送到子组件Copy
。displayMsg
的setter方法setDisplayMsg
在不同的计算阶段被调用。然而,我只看到Loading和80%Finished,而跳过了中间阶段,50%Finished。
具体来说,我的App
组件具有useEffect
,如下所示-
React.useEffect(() => {
setDisplayMsg("50% Finished");
simulateHeavyComputation(3000);
setDisplayMsg("80% Finished");
}, []);
其中
const simulateHeavyComputation = (sleepDuration) => {
var now = new Date().getTime();
while (new Date().getTime() < now + sleepDuration) {
/* do nothing */
}
};
在子组件中,消息50% Finished
作为prop成功发送,但从未呈现实际的DOM。
https://raw.githubusercontent.com/wchen408/js_react_status_indicator/main/asset/actual_behavior.gif
然而,我真正期待看到的是
https://raw.githubusercontent.com/wchen408/js_react_status_indicator/main/asset/expected_behavior.gif
我想知道是否有办法将simulateHeavyComputation
中完成的工作委托给工作线程,这样主线程就不会被阻止渲染DOM。非常感谢您的阅读!
此部分在应用程序中创建无限循环:while (new Date().getTime() < now + sleepDuration)
我会通过使用async/await
和超时Promise
来解决这个问题
首先,创建一个sleep
函数,该函数将在x
毫秒后解析
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
添加一个功能来启动模拟:
const startSimulation = async () => {
await sleep(1000);
// update msg after 1s
setDisplayMsg("50% Finished");
// update msg after 3s
await sleep(3000);
setDisplayMsg("80% Finished");
};
并在useEffect
中执行
React.useEffect(() => {
startSimulation();
}, []);
https://codesandbox.io/s/rough-wind-1k6r6?file=/src/App.js:991-1302
我认为您想要实现的是一个实时进度条。为此,您需要某种数据(可能来自服务器(,用作useEffect挂钩的依赖项。
在下面的代码中,我使用computation((和setTimeout模拟这些数据。
const [displayMsg, setDisplayMsg] = React.useState("Loading");
const [progress, setProgress] = React.useState(1);
computation();
function computation() {
const prog = setTimeout(() => {
setProgress(() => progress + 1);
}, 40);
if (progress === 100) {
clearTimeout(prog);
}
}
现在,您可以使用字符串模板返回进度,如下所示。它将显示实际百分比,而不是在每个步骤之间等待50%>80%。因此,用户总是知道应用程序仍在处理:
return (
<div className="App">
<h1>Message</h1>
<h2>`Progress: ${progress}%`</h2>
</div>
);
}
如果你想使用消息系统或其他副作用,比如填充视觉进度条,那么你可以使用类似于你的例子的useEffect挂钩。但是,如果将[]填充为依赖项,则useEffect挂钩将仅在第一次渲染时执行。在我上面的例子中,您可以使用进度useState作为依赖项,并根据状态显示消息。见下文:
React.useEffect(() => {
if (progress <= 50) {
setDisplayMsg(() => "Working up to 50%");
} else if (progress >= 50 && progress <= 80) {
setDisplayMsg(() => "Working up to 80%");
} else if (progress >= 80 && progress <= 99) {
setDisplayMsg(() => "Finishing up");
} else if (progress === 100) {
setDisplayMsg(() => "DONE");
}
}, [progress]);
你可以在这里测试代码
如果你在后端使用socket.io,那么你可以将包分成数字块,并在客户端接收数据,如下所示,同时设置进度:
socket.on("countedData", (count)=> {
setProgress(() => count)
})
我希望这能有所帮助。这是一个进度条包,你可以使用它来获得一个视觉进度条