Skip to content

Conversation

@xcqtnr
Copy link

@xcqtnr xcqtnr commented Feb 1, 2026

Summary

Fixes the infinite loading loop on the /skills page when a user scrolls to the bottom and stays idle.

Problem

The IntersectionObserver for infinite scroll was firing continuously while data was being loaded, causing an endless cycle of WebSocket queries to the Convex backend.

The loop:

User at bottom → Observer fires → Load request starts
       ↑                                    ↓
       └──── Observer still visible ←── Data arrives, list re-renders

Solution

Added isLoadingMore guard to the IntersectionObserver useEffect. The observer now won't be set up while a request is in progress, breaking the infinite loop.

Changes in src/routes/skills/index.tsx:

  useEffect(() => {
+   // Don't set up observer while loading to prevent infinite request loop
-   if (!canLoadMore || typeof IntersectionObserver === 'undefined') return
+   if (!canLoadMore || isLoadingMore || typeof IntersectionObserver === 'undefined') return
    // ... observer setup
- }, [canLoadMore, loadMore])
+ }, [canLoadMore, isLoadingMore, loadMore])

Related Issues


Additional Context: Sorting Issue (#85)

While investigating, I found that sorting happens client-side (lines 163-189). The server always returns data sorted by updatedAt regardless of user's sort selection.

This causes items to "jump around" when new pages load, which contributed to the infinite loop via re-renders.

Why server-side sorting is complex

Current indexes don't support both soft-delete filtering AND custom sorting:

Index Filters Deleted? Custom Sort?
by_active_updated ❌ (only updatedAt)
by_stats_downloads

A full fix would require new composite indexes:

.index('by_active_downloads', ['softDeletedAt', 'statsDownloads'])
.index('by_active_stars', ['softDeletedAt', 'statsStars'])

Happy to help implement this if maintainers are interested!

Testing

  • Verified fix logic through code review
  • Existing tests pass
  • Full local testing requires Convex environment

Greptile Overview

Greptile Summary

This PR fixes an infinite loading loop on the /skills route by preventing the IntersectionObserver infinite-scroll hook from being (re)attached while an in-flight page load is ongoing. Concretely, it adds an isLoadingMore guard to the observer useEffect and includes isLoadingMore in the dependency array, avoiding repeated loadMore() calls triggered by re-renders while the sentinel remains intersecting.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk.
  • The change is small, localized to the /skills infinite-scroll observer effect, and adds a straightforward guard plus dependency update to prevent repeated pagination triggers during an active load. No API contracts or data handling semantics change.
  • No files require special attention

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

Context used:

  • Context from dashboard - AGENTS.md (source)

…ard to IntersectionObserver useEffect to preventcontinuous WebSocket queries when user is idle at bottom of page.The observer now won't set up while a request is in progress, breakingthe infinite loop cycle.Fixes: Related to openclaw#89
@vercel
Copy link
Contributor

vercel bot commented Feb 1, 2026

@xcqtnr is attempting to deploy a commit to the Amantus Machina Team on Vercel.

A member of the Team first needs to authorize it.

@xcqtnr xcqtnr changed the title fix: prevent infinite loading loop on skills page #89 fix: prevent infinite loading loop on skills page Feb 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 Infinite Loading Loop on Skills Page (Related to #85)

1 participant