Open fridays opened 7 years ago
you can implement this with something like lodash.debounce
:
const debounce = require('lodash.debounce')
const start = debounce(NProgress.start, 80)
On second thought, that doesn't really cover it!
Looking forward for this great new feature! Use case:
A singlepage-app may have pages with zero loading time but the progress bar is shown anyway which is kind of confusing to the user. Would be nice to have a delay exactly as @fridays described: Only show the progress bar if NProgress.done()
has not been called within this time.
I would recommend to set the delay on NProgress default to 50-250ms.
Currently i have to do it manually which is not a big deal but integrated within NProgress would be much cleaner:
progressTimeout;
// [...]
switch (routeEvent) {
case NavigationStart:
this.progressTimeout = setTimeout(() => {
NProgress.start();
}, 250);
break;
case NavigationEnd:
case NavigationCancel:
case NavigationError:
clearTimeout(this.progressTimeout);
NProgress.done();
break;
}
Extend the lib before you use it:
NProgress.doStart = NProgress.start;
NProgress.doDone = NProgress.done;
NProgress.clearDelay = function () {
if (this.startDelay) {
clearTimeout(this.startDelay);
this.startDelay = undefined;
}
}
NProgress.start = function () {
this.clearDelay();
this.startDelay = setTimeout(function () {
NProgress.doStart();
}, this.settings.delay || 0);
};
NProgress.done = function () {
this.clearDelay();
this.doDone();
};
and then:
NProgress.configure({showSpinner: false, delay: 250}).start();
I've extended @fracz idea to avoid patching the library, in case anyone else finds it useful:
let progressBarTimeout = null;
const clearProgressBarTimeout = () => {
if (progressBarTimeout) {
clearTimeout(progressBarTimeout);
progressBarTimeout = null;
}
};
const startProgressBar = () => {
clearProgressBarTimeout();
progressBarTimeout = setTimeout(() => {
nprogress.start();
}, 200);
};
const stopProgressBar = () => {
clearProgressBarTimeout();
nprogress.done();
};
just use startProgressBar
and stopProgressBar
instead of nprogress.start/done
@msurdi I believe you could simplify that to:
let progressBarTimeout = null;
const startProgressBar = () => {
clearTimeout(progressBarTimeout);
progressBarTimeout = setTimeout(nprogress.start, 200);
};
const stopProgressBar = () => {
clearTimeout(progressBarTimeout);
nprogress.done();
};
To quote https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/clearTimeout:
Passing an invalid ID to clearTimeout() silently does nothing; no exception is thrown.
And https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout says:
It is guaranteed that a timeout ID will never be reused by a subsequent call to setTimeout() or setInterval() on the same object (a window or a worker). However, different objects use separate pools of IDs.
So it's fine to run clearTimeout
without checking first – it won't explode on null or old values, and values won't be reused.
For typescript users:
let progressBarTimeout: number | undefined = undefined
const startProgressBar = () => {
clearTimeout(progressBarTimeout)
progressBarTimeout = setTimeout(nProgress.start, 200)
}
const stopProgressBar = () => {
clearTimeout(progressBarTimeout)
nProgress.done()
}
For React you can use this component:
// /src/components/Progress.tsx
import NProgress from "nprogress";
import { useEffect } from "react";
export const Progress = () => {
// config
NProgress.configure({
showSpinner: false,
});
useEffect(() => {
const timeout = setTimeout(NProgress.start, 250); // 250ms
return () => {
clearTimeout(timeout);
NProgress.done();
};
});
return;
}
And then show it when something is loading:
// /src/somePageOrComponent.tsx
import { Progress } from "@/components/Progress";
...
return (
<>
{
somethingIsLoading ? <Progress /> : null;
}
<h1>Hello World</h1>
</>
)
...
Or with Suspense
import { Progress } from "@/components/Progress";
...
<Suspense fallback={<Progress />}>
<MyAsyncComponent />
</Suspense>
...
In Next.js you can use the same component to show nprogress on route changes. Just create a loading.ts file and export the component (For more information see loading.js file convention for Next.js App Router):
// /src/app/my/route/loading.ts
import { Progress } from "@/components/Progress";
export default Progress;
It would be nice to have a delay option, which will only show the progress bar if
.done
was not already called within the delay time: