Skip to content

Segment switcher inconsistent behaviour #43

@amoscicki

Description

@amoscicki

Problem

The quick navigation pills (module buttons in the top-right) navigate to a segment but then immediately bounce back to the previous segment.

Image

Root Cause

The SegmentContext (src/routes/learn/-components/segment-context.tsx) duplicates URL state and causes navigation issues.

How it works currently:

  1. Context stores currentSegmentId in React state
  2. Every navigation click must call setCurrentSegmentId() before/during navigation
  3. A useEffect in _layout.tsx watches context and calls navigate() if there's a mismatch

The bug:

ModulePillButton in quick-navigation-bar.tsx uses <Link> to navigate but doesn't call setCurrentSegmentId(). The useEffect sees the mismatch (context has old ID, URL has new segment) and navigates back.

Why the context is redundant:

  1. URL is already the source of truth - TanStack Router handles navigation, loader fetches segment data
  2. Components get currentSegmentId as a prop from loader data (segment.id), not from context
  3. Only _layout.tsx reads from context - and only for the sync useEffect
  4. The context is just an indirect way to call navigate()

Proposed Solutions

Option A: Remove context, use direct navigation

Delete SegmentContext entirely and use navigate() or <Link> directly:

// Before (VideoControls):
setCurrentSegmentId(nextSegment.id);

// After:
navigate({ to: "/learn/$slug", params: { slug: nextSegment.slug } });

For <Link> components - just remove the onClick handlers that call setCurrentSegmentId().

Pros: Simpler, URL is single source of truth
Cons: Components need access to useNavigate() or use <Link>

Option B: Keep context, move navigation logic into it

Make the context handle navigation internally so components don't need prop drilling:

// segment-context.tsx
export function SegmentProvider({ children }: { children: React.ReactNode }) {
  const navigate = useNavigate();
  
  const navigateToSegment = useCallback((segmentSlug: string) => {
    navigate({ to: "/learn/$slug", params: { slug: segmentSlug } });
  }, [navigate]);

  return (
    <SegmentContext.Provider value={{ navigateToSegment }}>
      {children}
    </SegmentContext.Provider>
  );
}

// Usage in components:
const { navigateToSegment } = useSegment();
navigateToSegment(nextSegment.slug);

Pros: No prop drilling for navigation, centralized navigation logic
Cons: Extra abstraction layer

Files affected:

  • src/routes/learn/-components/segment-context.tsx
  • src/routes/learn/$slug/_layout.tsx
  • src/routes/learn/$slug/-components/video-controls.tsx
  • src/routes/learn/$slug/-components/quick-navigation-bar.tsx
  • src/routes/learn/-components/navigation-items.tsx
  • src/routes/learn/-components/desktop-navigation.tsx

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions