javascript - 如何在 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
。它正在呼唤进步,但不是以适当的方式。0
就像每次我调用 onChange时都应该开始进步,并且应该继续增加。但是第一次从开始0
但它永远不会继续增加,然后它永远不会从0开始。我不知道我做错了什么。任何人都可以帮忙吗?
解决方案
似乎是因为关闭,useEffect()
在挂载时捕获了初始值并且从不更新它。因为Child
已经挂载并且useEffect()
在 mount 之后没有被调用,所以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/
推荐阅读
- java - 使用自定义分隔符列解析 CSV 未正确映射到 POJO
- c++ - 使用迭代器在地图中搜索值
- c# - ASP.NET MVC 获取具有特定属性的数据库条目列表
- reactjs - 如何正确导入 js 库(gigraph)到 react
- mongodb - 在 MongoDB 组之后选择每个字段的前 3 个
- android - 在安卓设备上启动的问题。react-native-navigation 出错
- google-apps-script - 下面的代码是否有任何替代方法可以在谷歌电子表格中附加文件(折旧错误(UIapp))?
- python - 如何修复 ValueError:没有足够的值在 python 中解包(预期 2,得到 1)?
- docker - 在 docker 中在 Mesos 上运行 Spark 时出错
- asp.net-core - Microsoft.Bot.Builder.Azure 不包含类型或命名空间“AzureTableStorage”