Skip to content

Commit 17b8368

Browse files
committed
Dashboard: Improve Progress bar implmentation with useTransition hook (#6769)
<!-- ## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes" If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000): ## Notes for the reviewer Anything important to call out? Be sure to also clarify these in your comments. ## How to test Unit tests, playground, etc. --> <!-- start pr-codex --> --- ## PR-Codex overview This PR refines the `useDashboardRouter` functionality by implementing a progress bar during route transitions and removing the use of `LoadingRouteHref`. It introduces `RouteStartTransitionFn` for managing transition states and modifies the `DashboardRouterTopProgressBar` to reflect these changes. ### Detailed summary - Removed `usePathname` and `useSearchParams` from `useRouterLoadingStatus`. - Replaced `LoadingRouteHref` with `RouteStartTransitionFn` for managing transition states. - Updated `replace` and `push` methods in `useDashboardRouter` to use `startTransition`. - Refactored `DashboardRouterTopProgressBarInner` to use `isRouteLoading`. - Adjusted progress bar visibility and state management based on `isRouteLoading`. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent 8ee427d commit 17b8368

File tree

1 file changed

+29
-49
lines changed

1 file changed

+29
-49
lines changed
Lines changed: 29 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,64 @@
11
"use client";
22

33
// eslint-disable-next-line no-restricted-imports
4-
import { usePathname, useRouter, useSearchParams } from "next/navigation";
4+
import { useRouter } from "next/navigation";
55
import {
6-
Suspense,
6+
type TransitionStartFunction,
77
useEffect,
88
useMemo,
99
useRef,
1010
useState,
1111
useSyncExternalStore,
12+
useTransition,
1213
} from "react";
1314
import { createStore } from "./reactive";
1415

1516
// Using useDashboardRouter instead of useRouter gives us a nice progress bar on top of the page when navigating using router.push or router.replace
1617

17-
// using a store instead of context to avoid triggering re-renders on root component
18-
const LoadingRouteHref = createStore<string | undefined>(undefined);
18+
const RouteStartTransitionFn = createStore<TransitionStartFunction>((fn) =>
19+
fn(),
20+
);
1921

2022
export function useDashboardRouter() {
23+
const startTransition = useSyncExternalStore(
24+
RouteStartTransitionFn.subscribe,
25+
RouteStartTransitionFn.getValue,
26+
RouteStartTransitionFn.getValue,
27+
);
28+
2129
const router = useRouter();
2230
return useMemo(() => {
2331
return {
2432
...router,
2533
replace(href: string, options?: { scroll?: boolean }) {
26-
LoadingRouteHref.setValue(href);
27-
router.replace(href, options);
34+
startTransition(() => {
35+
router.replace(href, options);
36+
});
2837
},
2938
push(href: string, options?: { scroll?: boolean }) {
30-
LoadingRouteHref.setValue(href);
31-
router.push(href, options);
39+
startTransition(() => {
40+
router.push(href, options);
41+
});
3242
},
3343
};
34-
}, [router]);
44+
}, [router, startTransition]);
3545
}
3646

37-
function useRouterLoadingStatus() {
38-
const loadingHref = useSyncExternalStore(
39-
LoadingRouteHref.subscribe,
40-
LoadingRouteHref.getValue,
41-
LoadingRouteHref.getValue,
42-
);
43-
const pathname = usePathname();
44-
const searchParams = useSearchParams();
45-
const searchParamsStr = searchParams?.toString();
46-
47-
const routerHref = pathname + (searchParamsStr ? `?${searchParamsStr}` : "");
48-
const isLoading = loadingHref && loadingHref !== routerHref;
47+
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
4948

50-
// reset loading on route load
49+
export function DashboardRouterTopProgressBar() {
50+
const [isRouteLoading, startTransition] = useTransition();
5151
// eslint-disable-next-line no-restricted-syntax
5252
useEffect(() => {
53-
if (!isLoading) {
54-
LoadingRouteHref.setValue(undefined);
55-
}
56-
}, [isLoading]);
57-
58-
return isLoading;
59-
}
53+
RouteStartTransitionFn.setValue(startTransition);
54+
}, []);
6055

61-
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
62-
63-
// Render this on root
64-
function DashboardRouterTopProgressBarInner() {
65-
const isLoading = useRouterLoadingStatus();
6656
const [progress, setProgress] = useState(0);
67-
6857
const progressStartedRef = useRef(false);
6958

7059
// eslint-disable-next-line no-restricted-syntax
7160
useEffect(() => {
72-
if (!isLoading) {
61+
if (!isRouteLoading) {
7362
setProgress(0);
7463
progressStartedRef.current = false;
7564
return;
@@ -85,7 +74,7 @@ function DashboardRouterTopProgressBarInner() {
8574

8675
async function updateProgressBar(progress: number, delay: number) {
8776
if (!isMounted) {
88-
if (!isLoading) {
77+
if (!isRouteLoading) {
8978
setProgress(100);
9079
}
9180
return;
@@ -112,14 +101,14 @@ function DashboardRouterTopProgressBarInner() {
112101
return () => {
113102
isMounted = false;
114103
};
115-
}, [isLoading]);
104+
}, [isRouteLoading]);
116105

117-
const width = isLoading ? progress : 100;
106+
const width = isRouteLoading ? progress : 100;
118107
return (
119108
<span
120109
className="fixed top-0 block h-[3px] bg-foreground"
121110
style={{
122-
opacity: isLoading ? "100" : "0",
111+
opacity: isRouteLoading ? "100" : "0",
123112
width: `${width}%`,
124113
transition: width === 0 ? "none" : "width 0.2s ease, opacity 0.3s ease",
125114
zIndex: "100000000",
@@ -128,12 +117,3 @@ function DashboardRouterTopProgressBarInner() {
128117
/>
129118
);
130119
}
131-
132-
// need to wrap with suspense because of useSearchParams usage
133-
export function DashboardRouterTopProgressBar() {
134-
return (
135-
<Suspense fallback={null}>
136-
<DashboardRouterTopProgressBarInner />
137-
</Suspense>
138-
);
139-
}

0 commit comments

Comments
 (0)