fix(dashboard): persist sidebar scroll position across navigation#4163
fix(dashboard): persist sidebar scroll position across navigation#4163chandan-1427 wants to merge 4 commits into
Conversation
|
@chandan-1427 is attempting to deploy a commit to the Hatchet Team on Vercel. A member of the Team first needs to authorize it. |
BloggerBust
left a comment
There was a problem hiding this comment.
Thanks for working on this @chandan-1427,
I left a couple of comments. The main issue is that restoring whenever scrollPosition changes can create unnecessary churn because scrollPosition is updated by the scroll handler itself. Otherwise, your fix looks correct.
| setIsResizing(false); | ||
| }, [setStoredCollapsed, storedCollapsed]); | ||
|
|
||
| useEffect(() => { |
There was a problem hiding this comment.
I think this restoration effect should avoid depending on scrollPosition. Since scrollPosition is updated by the scroll handler, this effect runs after every debounced scroll save. It also sets scrollTop programmatically, and can trigger another scroll event. The value may stabilize, but it creates unnecessary save -> render -> restore -> scroll-event churn.
Could we restore the scroll position on mount or after navigation instead of every time the saved scroll position changes? Also, useLayoutEffect would be better for restoring scroll position because it runs before paint and avoids a visible jump from the top back to the saved position.
| if (scrollSaveTimer.current) { | ||
| clearTimeout(scrollSaveTimer.current); | ||
| } | ||
| scrollSaveTimer.current = setTimeout(() => { |
There was a problem hiding this comment.
Can we add cleanup for this timer on unmount? that would avoid calling setScrollPosition after unmount if a debounce is still pending.
| setCollapsed(!collapsed); | ||
| }, [collapsed, setCollapsed]); | ||
|
|
||
| const [scrollPosition, setScrollPosition] = useLocalStorageState( |
There was a problem hiding this comment.
Do we need localStorage for this? The sidebar appears to stay mounted across these route changes, so a ref-only approach may be enough to preserve scroll position during navigation. That would avoid React state updates and localStorage writes while the user scrolls.
Thanks for the detailed review @BloggerBust , all your points are valid. The scroll position is naturally preserved by the DOM and no explicit save/restore is needed at all. I'll verify this is the case and if so, remove the scrollPosition/setScrollPosition state and localStorage key entirely, along with the restore effect and the debounced save. If there's a remount edge case where a ref fallback is needed, I'll make sure to add proper timer cleanup too. |
Description
When navigating between routes using the sidebar (especially Settings items),
the sidebar scroll position resets to the top instead of maintaining its
previous position.
Steps to Reproduce
Expected
Sidebar maintains its scroll position after navigation
Root Cause
The sidebar scroll container has no scroll position persistence. On re-render
triggered by route change, the scroll position resets to 0.
Fix
scrollPositionandsetScrollPositiontouseSidebarhook usingexisting
useLocalStorageStatepattern (consistent with howcollapsedand
expandedWidthare persisted)useEffectlocalStorage writes
Files Changed
frontend/app/src/components/hooks/use-sidebar.tsxfrontend/app/src/components/v1/nav/side-nav.tsx🤖 AI Disclosure
in accordance with Hatchet's AI_POLICY.md.