javascript - React.js - 具有延迟和防闪烁的加载指示器
问题描述
如何仅在加载状态超过1s 时才显示加载指示器,但是当它超过 1s 并在 2s 之前解决时,在 React 中显示加载指示器至少 1s 持续时间?
Angular JS 也存在类似的问题——它有这 5 个条件
- 如果数据早于 1 秒到达成功,则不应显示任何指示符(应正常渲染数据)
- 如果调用早于 1 秒失败,则不应显示任何指示符(应呈现错误消息)
- 如果数据在 1 秒后到达,则指示器应显示至少 1 秒(为防止闪烁的微调器,应在之后呈现数据)
- 如果呼叫在 1 秒后失败,则应显示至少 1 秒的指示符
- 如果通话时间超过 10 秒,则应取消通话(并显示错误消息)
我怎样才能在 React.js 中实现类似的东西?
我的自定义钩子:
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
const next = async () => {
setLoading(true); //can i set this to be true
//only if updateCurrent function takes more than 1s?
updateCurrent(code) //some async function
.then(() => setLoading(false))
.catch((e) => {
setLoading(false);
setError(e);
});
};
或者,如果我有一个 Loader 组件,我可以在它呈现之前添加 1s 的延迟并且在 1s 完成之前不要卸载?
解决方案
您可以执行以下操作(沙箱):
import React, { useState, useRef, useEffect, useCallback } from "react";
import { Spinner } from "react-bootstrap";
export default function TestComponent(props) {
const isMounted = useRef(true);
const [text, setText] = useState("");
const [isFetching, setIsFetching] = useState(false);
const [showLoader, setShowLoader] = useState(false);
useEffect(() => {
return () => {
isMounted.current = false;
};
}, []);
const fetchJSON = useCallback((url) => {
(async () => {
let shown;
const showTimer = setTimeout(() => {
shown = true;
isMounted.current && setShowLoader(true);
}, 1000);
const controller = new AbortController();
const timeoutTimer = setTimeout(() => controller.abort(), 10000);
try {
setIsFetching(true);
const response = await fetch(url, { signal: controller.signal });
const json = await response.json();
isMounted.current && setText(JSON.stringify(json));
} catch (err) {
isMounted.current && setText(err.toString());
} finally {
clearTimeout(timeoutTimer);
isMounted.current && setIsFetching(false);
if (shown) {
setTimeout(() => {
isMounted.current && setShowLoader(false);
}, 1000);
} else {
clearTimeout(showTimer);
}
}
})();
}, []);
return (
<div className="component">
<div className="caption">Demo:</div>
<div>
{showLoader ? <Spinner animation="border" variant="primary" /> : text}
</div>
<button
className="btn btn-success"
onClick={() => fetchJSON(props.url)}
disabled={isFetching}
>
{isFetching ? "Fetching..." : "Fetch data"}
</button>
</div>
);
}
或使用自定义库集(Codesandbox 演示):
import React, { useState } from "react";
import { useAsyncCallback, E_REASON_UNMOUNTED } from "use-async-effect2";
import { CPromise, CanceledError } from "c-promise2";
import cpAxios from "cp-axios";
import { ProgressBar } from "react-bootstrap";
export default function TestComponent(props) {
const [text, setText] = useState("");
const [progress, setProgress] = useState(0);
const [isFetching, setIsFetching] = useState(false);
const [showLoader, setShowLoader] = useState(false);
const fetchUrl = useAsyncCallback(function* (options) {
setIsFetching(true);
setProgress(0);
setText("");
this.progress(setProgress);
let loaderShown;
const loaderPromise = CPromise.delay(1000).then(() => {
loaderShown = true;
setShowLoader(true);
});
try {
this.innerWeight(2); // total weight for progress calculation
const response = yield cpAxios(options).timeout(props.timeout);
loaderPromise.cancel();
yield CPromise.delay(3000); // just for fun
setText(JSON.stringify(response.data));
} catch (err) {
loaderPromise.cancel();
CanceledError.rethrow(err, E_REASON_UNMOUNTED);
setText(err.toString());
}
setIsFetching(false);
if (loaderShown) {
yield CPromise.delay(1000);
setShowLoader(false);
}
});
return (
<div className="component">
<div className="caption">useAsyncEffect demo:</div>
<div>{showLoader ? <ProgressBar now={progress * 100} /> : text}</div>
{!isFetching ? (
<button
className="btn btn-success"
onClick={() => fetchUrl(props.url)}
disabled={isFetching}
>
Fetch data
</button>
) : (
<button
className="btn btn-warning"
onClick={() => fetchUrl.cancel()}
disabled={!isFetching}
>
Cancel request
</button>
)}
</div>
);
}
推荐阅读
- xamarin.android - 编写一个调用另一个 App 元素的宏
- internationalization - 如何翻译 RestructuredText 中的链接?
- javascript - Solidity:错误:请将数字作为字符串或 BN 对象传递以避免精度错误
- javascript - ReferenceError:频道未使用 discord.js 定义
- javascript - inputType = {“number”},数字拨号盘未在ios中显示但在android中可见 - React js
- r - 查找值是否在其他列的范围内
- java - 使用两个活动配置文件运行 Spring Boot
- spring-boot - 通过 Web 访问 Heroku 上部署的后端服务创建的新图像?
- perl - 搜索键值对并将值附加到 unix 中的其他键
- oracle - 在 Azure 中为 Oracle DB 捕获插入、更新和删除事件