首页 > 解决方案 > 如何在 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,它都应该调用进度,因为我将其设置progresstrue。它正在呼唤进步,但不是以适当的方式。0就像每次我调用 onChange时都应该开始进步,并且应该继续增加。但是第一次从开始0但它永远不会继续增加,然后它永远不会从0开始。我不知道我做错了什么。任何人都可以帮忙吗?

标签: javascriptreactjsreact-hooksprogress

解决方案


似乎是因为关闭,useEffect()在挂载时捕获了初始值并且从不更新它。因为Child已经挂载并且useEffect()在 mount 之后没有被调用,所以setInterval初始值为progress0 并且不会更新进度。

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

问题是 useEffect 从第一次渲染中捕获计数。它等于 0。我们从不重新应用效果,因此 setInterval 中的闭包始终引用第一次渲染的计数,并且 count + 1 始终为 1。哎呀!

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

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

您可以参考 Dan 的博客以获得清晰的理解 https://overreacted.io/making-setinterval-declarative-with-react-hooks/


推荐阅读