So this ended up being a nasty “race condition” between the “sentinel” we use to detect when we need to trigger the “load more” action and the rendering of the rows in the users directory
The Issue
When there were at least 50 users, the /u
(users directory) page was triggering loadMore
immediately on first load, before the user could scroll down. This caused an unwanted second page of results to load automatically.
Root Cause
Timing race condition during initial page load:
- User navigates to
/u
controllers/users
starts loading data (isLoading: true
)- Template renders with
ConditionalLoadingSpinner
showing spinner - Data arrives,
isLoading
becomesfalse
- Spinner hides,
DirectoryTable
begins rendering 50 users - LoadMore sentinel element is inserted into DOM
- IntersectionObserver is created and starts watching immediately
- At this moment, the table is still calculating layout/expanding to full height
- Sentinel is briefly visible in viewport (~292px from top) before table expands
- Observer detects intersection → triggers
loadMore
- Table finishes expanding to full height (~3689px)
- Sentinel moves to correct position (~3959px, below viewport)
The observer was “too eager” - it started watching before the content finished its layout, catching the sentinel during the brief moment when the table hadn’t reached its final height yet.
The Fix
Delay observer creation until content is ready:
Added an @isLoading
parameter to LoadMore
that prevents the IntersectionObserver
from being created when content is still loading.
How It Works Now
Page load → isLoading=true → Modifier skips observer creation
↓
Data loads → isLoading=false → Modifier re-runs, creates observer
↓
Table fully expanded → Sentinel at correct position (below viewport)
↓
User scrolls down → Sentinel enters viewport → loadMore triggers ✓