如何在 React JS 中实现进度条?

问题描述

我正在尝试在我的 react js 项目中实现一个进度条,但它无法正常工作。 我实现了进度条的子组件的代码 -

export const Child = (props: ChildProps): React.ReactElement => {
    const { text,showProgress,typeId } = props;
    const [progress,setProgress] = React.useState<number>(0);
    
    React.useEffect(() => {
        const timer = setInterval(() => {
          setProgress((oldProgress) => {
            if (oldProgress === 100) {
              return 0;
            }
            const diff = Math.random() * 100;
            return Math.min(oldProgress + diff,100);
          });
        },500);
    
        return () => {
            if(progress === 100){
                clearInterval(timer);
            }
          
        };
      },[]);
      
      return (
        <Grid container>
            <Grid container item direction="column">
                <Label>{text}</Label>
                <Select
                    options={myOptions}
                    value={mySelectedValue}
                    onChange={(selectedOption: Select<string,string>) =>
                        onChange('TYPE',selectedOption.value,typeId)
                    }
                />
            </Grid>
            {showProgress && (<ProgressBarContainer>
                <ProgressBar color={'Blue'} height={'Small'} progress={progress} />
            </ProgressBarContainer>)
            }
        </Grid>
    );
}

我在父组件中为 onChange 函数定义了一个回调。每次我调用 onChange 事件时,它都应该显示进度。 父组件的代码 -

const [showProgress,setShowProgress] = React.useState<boolean>(false);
const [isApiCalled,setIsApiCalled] = React.useState<boolean>(false);
const [myData,setMyData] = React.useState<any>([]);

const onChange = (type: string,value: string,typeid: number): void => {
    if (type === 'TYPE') {
        setShowProgress(true);
        setIsApiCalled(true);
    }
    setMyData(updateData); // I haven't written the code for it here.
}

const saveData = (isSave: boolean): void => {
        callAPI(saveRequestData); // calling API
        setShowProgress(false);
    }
React.useEffect(()=> {
        saveData(true)
        setIsApiCalled(false);
    },[isApiCalled])
    
return (
            <Child myData={myData} onChange={onChange} showProgress={showProgress} />;
        );
    

每次我更改 Child 中的数据时,它都应该调用进度,因为我将 progress 设置为 true。它正在呼吁进展,但不是以适当的方式。每次我调用 onChange 时,进度都应该从 0 开始,并且应该继续增加。但是第一次它从 0 开始,但它永远不会继续增加,之后它永远不会从 0 开始。我不知道我做错了什么。有人可以帮忙吗?

解决方法

在我的应用中,我也包含了一个进度条,更具体地说是一个圆形进度条。我使用了一个已经开发的包。您可以按照此处的说明进行操作:https://www.npmjs.com/package/react-circular-progressbar

一个简单的实现:

import { CircularProgressbar } from "react-circular-progressbar";

const MyComponent: React.FC = () => {
const points = 30000 //only of testing
 const percentage = (points / 10000) * 100; //calculate the percentage
 return (
        <div>
            <CircularProgressbar value={percentage} text={points} />
        </div>
    );
}
export default MyComponent;

对于自定义样式和 CSS,您可以在此处复制和修改代码:https://github.com/kevinsqi/react-circular-progressbar/blob/HEAD/src/styles.css

,

似乎是因为闭包,useEffect() 会在挂载时捕获那里的初始值并且从不更新它。由于 Child 已经被挂载并且 useEffect() 在挂载后没有被调用,因此 setInterval 的初始值 progress 总是为 0 并且进度没有更新。

Dan Abramov 在他的博客中解释了类似的问题: 摘自他关于这个问题的博客

问题是 useEffect 从第一个开始捕获计数 使成为。它等于 0。我们从不重新应用效果,因此闭包 在 setInterval 中总是引​​用第一次渲染的计数,并且 count + 1 总是 1。糟糕!

为了解决这个问题,您可以使用 useRef 钩子捕获间隔内的回调,并在重新渲染时使用新的回调引用更新它。所以 setInterval 指向每个间隔后更新的回调。类似的东西:

 useEffect(() => {
    savedCallback.current = callback;
  });

你可以参考Dan的博客来清楚了解 https://overreacted.io/making-setinterval-declarative-with-react-hooks/