Fix TOC groups losing expand/active state during client-side navigation (RND-11931)#4404
Draft
zenoachtig wants to merge 2 commits into
Draft
Fix TOC groups losing expand/active state during client-side navigation (RND-11931)#4404zenoachtig wants to merge 2 commits into
zenoachtig wants to merge 2 commits into
Conversation
The sidebar table of contents lives in the site layout, whose client subtree is
remounted on client-side navigation (already documented in useClearRouterCache,
Next.js #67542). PageGroupItem and ToggleableLinkItem kept their expand/collapse
state in component-local useState/useRef, so every navigation reset it: top-level
groups snapped back open, nested groups re-derived from the freshly-mounting active
path, and the descendants' enter animation replayed each time - which read as the
sidebar flickering / losing its expanded context.
Move the groups' open/collapsed state into a module-level store keyed by page id so
it survives the navigation remount (mirroring the existing module-level workaround in
useClearRouterCache and the zustand store in useCurrentPage). Until the visitor
toggles a group it follows the active page; once toggled, their choice is remembered
for the session. Also set AnimatePresence initial={false} so an already-open group
does not replay its expand animation on mount.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DaBLy53zu9DTce9sZhaARf
🦋 Changeset detectedLatest commit: b8e7ec1 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Contributor
|
The latest updates on your projects. Learn more about Argos notifications ↗︎
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Night-shift draft — not ready to merge; prepared for Zeno to review in the morning.
Linear: RND-11931 — "TOC feels unstable: groups expand/collapse or briefly lose context while navigating"
Proposed changes
Root cause. The sidebar table of contents is rendered inside the site layout (
SpaceLayout→TableOfContents), i.e. the shared(content)/layout.tsx. In this app the layout's client subtree is remounted on client-side navigation — this is already documented in the codebase:useClearRouterCache.tsxkeepspreviousContextIdin a module-level variable precisely because "the contextId gets reset on navigation … probably because of [Next.js #67542]" (auseRefthere is wiped on every navigation, which only happens if the component remounts).The two collapsible TOC components stored their expand/collapse state in component-local state, so that remount reset it on every navigation:
PageGroupItem.tsx—useState(true): a top-level group the visitor had collapsed snaps back open after navigating.ToggleableLinkItem.tsx—useState(defaultIsOpen)+ auseRef(hasBeenToggled)+ an effect: nested groups re-derived from the freshly-mounting active path, the visitor's manual toggles were forgotten, and because the descendants used<AnimatePresence>withinitial={hide}, the expand animation replayed on every mount — which reads as the sidebar flickering / losing its expanded context.Fix. Move the groups' open/collapsed state into a small module-level store (
useTOCGroupState, a zustand store keyed by page id) so it survives the navigation remount. This mirrors patterns already in the repo: the module-level workaround inuseClearRouterCacheand the zustandvisitedPagesStoreinuseCurrentPage.tsx.PageGroupItemandToggleableLinkItemnow read/write this store instead of local state; the now-redundanthasBeenToggledref + sync effect are removed.Descendantsuses<AnimatePresence initial={false}>so an already-open group renders without replaying its expand animation on mount; visitor-initiated toggles still animate (they happen after mount).No hydration risk: the store is empty on the server and on first client render, so the initial value equals the previous
defaultIsOpen/true.Changelog
For Zeno
page.idand lives for the SPA session (module-level, bounded by visited TOC items). Not persisted to storage — reloads start fresh, matching today's behavior.AnimatePresence initial={false}change affects only the enter animation on mount.bun run typecheckforgitbook(viaturbo, 27 tasks green),bun run format, andbiome checkon the changed files — all clean.useClearRouterCacheworkaround for the same "state reset on navigation" behavior. Suggested manual check: on a multi-section/VA site, collapse a TOC group, navigate to another page, and confirm the group stays collapsed and the sidebar doesn't flash.aria-expanded/data-activepersistence would be the right coverage but needs a fixture site whose layout actually remounts — worth a follow-up.Generated by Claude Code