fix(workspace): prevent URL sync from wiping params on hydration render

The URL sync effect and hydration effect run in the same React render
cycle. Since React state updates (setActivePath) are batched, the URL
sync effect still saw activePath=null and pushed "/", stripping all
query params. This caused an alternating refresh bug where odd refreshes
showed the homepage and even refreshes worked correctly.

Skip the URL sync effect for one render after hydration completes, giving
React state time to update before the effect writes the URL.
This commit is contained in:
kumarabhirup 2026-03-06 21:29:03 -08:00
parent 4578dfa945
commit 8f4fd62d63
No known key found for this signature in database
GPG Key ID: DB7CA2289CAB0167

View File

@ -365,6 +365,10 @@ function WorkspacePageInner() {
const searchParams = useSearchParams();
const router = useRouter();
const initialPathHandled = useRef(false);
// Counts how many renders have happened since hydration completed.
// The URL sync effect skips render 0 (the same render where hydration ran)
// because React state (activePath, etc.) hasn't updated yet.
const rendersSinceHydration = useRef(-1);
const lastPushedQs = useRef<string | null>(null);
// Chat panel ref for session management
@ -1165,6 +1169,13 @@ function WorkspacePageInner() {
useEffect(() => {
if (!initialPathHandled.current) return;
// Skip the render where hydration just ran — React state (activePath, etc.)
// hasn't updated yet, so we'd compute empty params and wipe the URL.
if (rendersSinceHydration.current === 0) {
rendersSinceHydration.current = 1;
return;
}
const current = new URLSearchParams(window.location.search);
const params = new URLSearchParams();
@ -1235,6 +1246,7 @@ function WorkspacePageInner() {
useEffect(() => {
if (initialPathHandled.current || treeLoading || tree.length === 0) return;
rendersSinceHydration.current = 0;
const urlState = parseUrlState(searchParams);
if (urlState.path) {