我尝试向用户报告承诺执行的进度。我遇到的问题是 React 似乎正在将我的
setState
调用合并到单个渲染中。
当我点击开始时,它闪烁
0 / 5
,然后保持1 / 5
,其他时候它会高于1。还有一个问题是按下按钮时执行停止,因为setState
没有是时候更新 total
了,从而在按下按钮之前停止代码(注释行)。下面的代码是我的尝试。
import React, { useState } from "react";
export default function ({}) {
const [state, setState] = useState({ progress: 0, total: 0 });
const wait = (duration) => () => new Promise((resolve) => setTimeout(resolve, duration));
const startWork = async () => {
const tasks = [wait(100), wait(200), wait(300), wait(400), wait(500)];
setState({ ...state, progress: 0, total: tasks.length });
for (const task of tasks) {
// stop if cancelled
// if (state.total === 0) return;
const data = await task();
setState({ ...state, progress: state.progress + 1 });
}
};
const cancelWork = () => {
setState({ ...state, total: 0 });
};
return (
<div>
<p>
{state.progress} / {state.total}
</p>
<button onClick={startWork}>Start</button>
<button onClick={cancelWork}>Cancel</button>
</div>
);
}
setState
更新下一次渲染的状态。当你这样做时
setState({ ...state, progress: 0, total: tasks.length });
for (const task of tasks) {
setState({ ...state, progress: state.progress + 1 });
}
state.progress
将是循环的每次迭代的初始值,因为它尚未重新渲染,因此每次您都将其设置为 0 + 1
(假设 state.progress 最初为 0)。
您可能想做的是:
let progress = state.progress || 0;
setState({ ...state, progress, total: tasks.length });
for (const task of tasks) {
progress += 1;
setState({ ...state, progress });
}
这样每次您调用
setState
时,您都会使用正确的进度值。