studio/frontend: clear stale thread/compare search after sidebar nav - #5633
studio/frontend: clear stale thread/compare search after sidebar nav#5633danielhanchen wants to merge 2 commits into
Conversation
…gation
The sidebar's "delete active chat", "New Chat", and Unsloth-home-logo
all call `navigate({to: "/chat", search: {new: nonce}})`. TanStack
Router merges search params by default, so passing only `{new}` leaves
the prior `thread=<id>` (or `compare=<id>`) in the URL. The recovery
useEffect in chat-page.tsx only fires when `search.thread` changes,
which it doesn't here, so the in-tab address bar stays stale until a
hard reload kicks in.
Visible impact: deleting the currently-open chat correctly removes the
row from the sidebar, but the URL still references the deleted id.
A copied share link is broken; opening a new tab to the same URL
trips the "Chat not found" recovery toast.
Fix: pass `thread: undefined, compare: undefined` alongside `new` so
the merged search ends up as just `{new: nonce}`. This is the
documented TanStack Router idiom for removing keys.
Probe + repro: scripts/r6_sidebar_delete_chat_probe.py and
scripts/r6_repro_active_delete_url.py.
There was a problem hiding this comment.
Code Review
This pull request updates the navigation logic in the AppSidebar component to explicitly clear thread and compare search parameters, preventing TanStack Router from merging stale values into the URL. The reviewer suggests using the functional form of the search property in TanStack Router as a more idiomatic and robust way to reset parameters. Furthermore, for the thread deletion handler, it is recommended to explicitly clear the active thread ID and close the mobile sidebar to maintain state consistency.
| // Clear `thread`/`compare` explicitly: TanStack Router merges | ||
| // search params by default, so passing only `{new}` leaves the | ||
| // deleted thread id in the URL. The recovery useEffect in | ||
| // chat-page only fires on hard reload, which means the in-tab | ||
| // address bar stays stale until then. | ||
| navigate({ | ||
| to: "/chat", | ||
| search: { new: view.newThreadNonce }, | ||
| search: { new: view.newThreadNonce, thread: undefined, compare: undefined }, | ||
| }); |
There was a problem hiding this comment.
To ensure consistency with other navigation actions in the sidebar and to avoid stale state in the store, you should clear the active thread ID and close the mobile sidebar (if open) when navigating after a deletion.
Additionally, using the functional form of search in TanStack Router is a more robust way to reset the URL parameters. It prevents the router from merging old thread or compare IDs into the new URL, ensuring the address bar is updated immediately without relying on the recovery logic in ChatPage (which only handles stale IDs on mount).
setActiveThreadId(null);
void navigate({
to: "/chat",
search: () => ({ new: view.newThreadNonce }),
});
closeMobileIfOpen();
References
- Prefer explicit cleanup of resource bindings or references within deletion handlers over background 'sweeper' effects that rely on list state to detect and clean up stale data.
| void navigate({ | ||
| to: "/chat", | ||
| search: { new: createNavigationNonce() }, | ||
| search: { new: createNavigationNonce(), thread: undefined, compare: undefined }, | ||
| }); |
There was a problem hiding this comment.
Using the functional form of search is a more idiomatic and robust way to reset the URL parameters in TanStack Router, as it explicitly defines the new search state rather than merging with the existing one.
| void navigate({ | |
| to: "/chat", | |
| search: { new: createNavigationNonce() }, | |
| search: { new: createNavigationNonce(), thread: undefined, compare: undefined }, | |
| }); | |
| void navigate({ | |
| to: "/chat", | |
| search: () => ({ new: createNavigationNonce() }), | |
| }); |
| navigate({ | ||
| to: "/chat", | ||
| search: { new: createNavigationNonce(), thread: undefined, compare: undefined }, | ||
| }); |
There was a problem hiding this comment.
Using the functional form of search is a more idiomatic and robust way to reset the URL parameters in TanStack Router, as it explicitly defines the new search state rather than merging with the existing one.
| navigate({ | |
| to: "/chat", | |
| search: { new: createNavigationNonce(), thread: undefined, compare: undefined }, | |
| }); | |
| void navigate({ | |
| to: "/chat", | |
| search: () => ({ new: createNavigationNonce() }), | |
| }); |
Address gemini review on #5633: use `search: () => ({...})` instead of merging with explicit `undefined`. The function form is the documented idiom for replacing (not merging) search state, and is clearer about intent. Also clear `activeThreadId` and close mobile sidebar in the delete-handler callback to match the other two sites.
Summary
navigate({to: "/chat", search: {new: nonce}}). TanStack Router merges search params by default, so the prior?thread=<id>(or?compare=<id>) survives.features/chat/chat-page.tsx:557-585only fires whensearch.threadchanges, so the in-tab URL stays stale until a hard reload.thread: undefined, compare: undefinedalongsidenewto actually remove the prior keys.Repro
Test plan