执行繁重计算时显示加载信息的正确 React 方式是什么?

问题描述 投票:0回答:2

我正在 React 中执行繁重的计算,并且我 (1) 不希望 UI 阻塞,并且 (2) 希望显示有关进程的信息。我认为

useEffect
 钩子是解决此问题的正确方法,但我没有得到正确的结果。

我期望发生的是“正在加载”部分将显示,百分比将从 0 逐渐运行到 100,完成后显示“已完成”。实际发生的情况是从 0 开始,然后在整个计数过程中什么都没有,然后不知从何跳到“已完成”。

执行此操作的正确方法是什么,既可以完成繁重的工作,又可以正确显示 UI 而不会阻塞任何内容?

import { useEffect, useRef, useState } from "react";

export default function App() {
  const [isLoading, setIsLoading] = useState<boolean>(true);
  const [loadingPercentage, setLoadingPercentage] = useState<number>(0);
  const loadingRef = useRef<any>();

  useEffect(() => {
    if (!loadingRef.current) {
      return;
    }

    const MAX_COUNT = 1000000000;
    let threshold = 0;
    for (let count = 0; count < MAX_COUNT; ++count) {
      ///
      /// Simulate some heavy-lifting process with process counter
      ///
      if (count > threshold) {
        const percentage = (count / MAX_COUNT) * 100;
        setLoadingPercentage(percentage);
        // console.log(percentage); <-- This just demonstrates that the % set actually gets hit
        threshold += MAX_COUNT / 100;
      }
    }

    setLoadingPercentage(100);
    setIsLoading(false);
  }, [loadingRef.current]);

  return (
    <div className="App">
      <h1>Counting a whole lot of numbers</h1>
      {isLoading ? (
        <div>
          <h2 ref={loadingRef}>{"Loading... " + loadingPercentage + "%"}</h2>
        </div>
      ) : (
        <h2>Finished counting!</h2>
      )}
    </div>
  );
}

javascript reactjs react-hooks
2个回答
0
投票

以下内容不一定是“好”答案,但就本问题而言,它可能是“最不坏”的答案。 正如 @matt-morgan 指出的,useEffect 中的循环阻止了 UI 线程上发生的任何事情,这就是中间阶段不显示的原因。正如 @David 所说,“正确”的方法可能是使用 Web Workers 来实现此目的。 更简单但不完美的解决方案可能是使用异步函数,并在某些阶段构建一些延迟,以便为 UI 线程提供一些空间来完成它的工作。这并不像后台线程那样完全解锁 UI,但至少它确实为 UI 提供了一些空间,因此它实际上会完成所需的 UI 更新。

import { useEffect, useRef, useState } from "react"; export default function App() { const [isLoading, setIsLoading] = useState<boolean>(true); const [loadingPercentage, setLoadingPercentage] = useState<number>(0); useEffect(() => { function delay(delayMs: number) { return new Promise((res) => setTimeout(res, delayMs)); } const doHeavyLiftingAsync = async () => { const MAX_COUNT = 1000000000; let threshold = 0; for (let count = 0; count < MAX_COUNT; ++count) { if (count > threshold) { const percentage = (count / MAX_COUNT) * 100; setLoadingPercentage(percentage); console.log(percentage); threshold += MAX_COUNT / 100; await delay(10); } } setLoadingPercentage(100); setIsLoading(false); }; doHeavyLiftingAsync(); }, []); return ( <div className="App"> <h1>Counting a whole lot of numbers</h1> {isLoading ? ( <div> <h2>{"Loading... " + loadingPercentage + "%"}</h2> </div> ) : ( <h2>Finished counting!</h2> )} </div> ); }


您可以使用 setTimeout 在进程加载时异步显示进度。

import React from 'react'; export function App(props) { const [isLoading, setIsLoading] = React.useState(true); const [progressPercentage, setProgressPercentage] = React.useState(0); const step = 1 const interval = 10 const maxProgress = 100 React.useEffect(() => { const updateProgress = () => setProgressPercentage(progressPercentage + step) if (progressPercentage < maxProgress) { setTimeout(updateProgress, interval) } else { setIsLoading(false); } },[progressPercentage]); return ( <div className='App'> <h2>Example of a progress bar while loading content</h2> {isLoading ? ( <div style={{borderColor:'red',borderWidth:2,borderStyle:'solid',backgrounColor:'black',padding:10,margin:50}} onClick={() => setProgressPercentage(0)}> <div style={{ backgroundColor:'white', color:'cyan', padding:10,width: progressPercentage + "%" }} /> <p>{props.name}</p> </div> ) : ( <h2>Finished loading!</h2> )} </div> ); }


0
投票

© www.soinside.com 2019 - 2024. All rights reserved.