簡體   English   中英

當主線程與其他操作排隊時,state 更改后 React 不會重新渲染組件?

[英]React doesn't re-render component after state changes when main thread is queued up with other operations?

代碼和更多說明 -這里

我正在開發一個計算量大的 web 應用程序,我希望用戶知道計算的進展,這樣應用程序就不會顯得陳舊。 基本上我在計算的 state 中有三個消息要指示 -

  • 正在加載
  • 50% 完成
  • 80% 完成

我在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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM