问题描述
我正在尝试在我的 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/