[英]React doesn't re-render component after state changes when main thread is queued up with other operations?
代碼和更多說明 -這里
我正在開發一個計算量大的 web 應用程序,我希望用戶知道計算的進展,這樣應用程序就不會顯得陳舊。 基本上我在計算的 state 中有三個消息要指示 -
我在App
組件中創建了一個displayMsg
state ,它作為道具發送到子組件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
function 將在x
毫秒后解決
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
添加一個 function 開始仿真:
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 作為依賴項,並根據 state 顯示消息。 見下文:
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,那么您可以將 package 划分為數字塊,並在設置進度時在客戶端接收如下數據:
socket.on("countedData", (count)=> {
setProgress(() => count)
})
我希望這有幫助。 這是一個進度條 package,您可以使用它來獲得可視進度條
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.