[PR #27546] fix: improve infinite scroll observer responsiveness #31798

Closed
opened 2026-02-21 20:50:09 -05:00 by yindo · 0 comments
Owner

Original Pull Request: https://github.com/langgenius/dify/pull/27546

State: closed
Merged: Yes


Update:
Incorporated automated code review suggestions from @gemini-code-assist.

  • Removed unnecessary observer?.disconnect() call.
  • Cleaned up dependency array for clarity and efficiency.

All tests passed and functionality remains consistent across different screen sizes.

🧩 Summary

Fixes the issue where the “Load More” (infinite scroll) feature in the Apps list fails to trigger on certain screen resolutions or container sizes.
The IntersectionObserver did not consistently detect the anchor element’s visibility due to a static rootMargin and missing explicit root definition.

This PR improves scroll detection responsiveness and ensures consistent behavior across devices.

Fixes #27541


🔧 Changes

  • Added dynamic calculation of rootMargin based on the container’s height.

    • Ensures that the scroll trigger adapts to various screen sizes.
    • Margin dynamically adjusts between 100px – 200px or 20% of container height.
  • Explicitly set the scroll container (containerRef.current) as the root for the IntersectionObserver.

  • Added preemptive cleanup (observer?.disconnect()) before creating a new observer to prevent duplicates.

  • Included containerRef in the dependency array to properly reinitialize the observer when the container changes.

  • Added clear English comments for maintainability and readability.


🧠 Reasoning

Previously, the observer relied on the viewport as the root and a fixed rootMargin of 100px.
On certain resolutions or when the container height was small, the anchor element never entered the margin area — preventing the callback from firing.
By making the rootMargin dynamic and binding the observer to the scroll container, the infinite scroll now triggers correctly on all screen sizes.


🧪 Steps to Reproduce

  1. Open the Apps list page.
  2. Resize the browser window to a smaller height (e.g., below 900px).
  3. Scroll to the bottom — observe that the “Load More” function no longer triggers.
  4. Apply this patch — observe that infinite scroll triggers as expected.

Expected Behavior (After Fix)

  • Infinite scroll now consistently triggers when the anchor element enters the viewport or scroll container.
  • Works across different resolutions and container heights.
  • No duplicate observers created when dependencies change.

🧩 Code Example

    if (anchorRef.current && containerRef.current) {
      // Calculate dynamic rootMargin based on container height for better responsiveness
      const containerHeight = containerRef.current.clientHeight
      const dynamicMargin = Math.max(100, Math.min(containerHeight * 0.2, 200)) // Between 100px and 200px, or 20% of container height

      observer = new IntersectionObserver((entries) => {
        if (entries[0].isIntersecting && !isLoading && !error && hasMore)
          setSize((size: number) => size + 1)
      }, {
        root: containerRef.current || null,
        rootMargin: `${dynamicMargin}px`,
        threshold: 0.1, // Trigger when 10% of the anchor element is visible
      })
      observer.observe(anchorRef.current)
    }
    return () => observer?.disconnect()
  }, [isLoading, setSize, anchorRef, data, error, containerRef])

🧰 Testing Notes

  • Verified on Chrome, Edge, and Safari.
  • Tested across resolutions: 720p, 1080p, 1440p, and 4K.
  • Confirmed no regression on normal scroll performance.
  • No console warnings or memory leaks observed.

🏷️ Type of Change

Type Description
🐛 Bug Fix Fixes the infinite scroll not triggering on some resolutions
💅 Improvement Adds dynamic margin and root handling for better UX
🧹 Refactor Minor cleanup and code comments added

🙌 Additional Notes

This change does not alter backend logic or API interactions.
It only improves frontend scroll event detection reliability.
Reviewed and tested locally before submission.

**Original Pull Request:** https://github.com/langgenius/dify/pull/27546 **State:** closed **Merged:** Yes --- > **Update:** > Incorporated automated code review suggestions from `@gemini-code-assist`. > > * Removed unnecessary `observer?.disconnect()` call. > * Cleaned up dependency array for clarity and efficiency. > > All tests passed and functionality remains consistent across different screen sizes. ## 🧩 Summary Fixes the issue where the **“Load More” (infinite scroll)** feature in the Apps list fails to trigger on certain screen resolutions or container sizes. The IntersectionObserver did not consistently detect the anchor element’s visibility due to a static `rootMargin` and missing explicit `root` definition. This PR improves scroll detection responsiveness and ensures consistent behavior across devices. **Fixes #27541** --- ## 🔧 Changes * Added dynamic calculation of `rootMargin` based on the container’s height. * Ensures that the scroll trigger adapts to various screen sizes. * Margin dynamically adjusts between **100px – 200px** or **20%** of container height. * Explicitly set the scroll container (`containerRef.current`) as the `root` for the IntersectionObserver. * Added preemptive cleanup (`observer?.disconnect()`) before creating a new observer to prevent duplicates. * Included `containerRef` in the dependency array to properly reinitialize the observer when the container changes. * Added clear English comments for maintainability and readability. --- ## 🧠 Reasoning Previously, the observer relied on the **viewport** as the root and a fixed `rootMargin` of `100px`. On certain resolutions or when the container height was small, the anchor element never entered the margin area — preventing the callback from firing. By making the `rootMargin` dynamic and binding the observer to the scroll container, the infinite scroll now triggers correctly on all screen sizes. --- ## 🧪 Steps to Reproduce 1. Open the **Apps list** page. 2. Resize the browser window to a smaller height (e.g., below 900px). 3. Scroll to the bottom — observe that the “Load More” function no longer triggers. 4. Apply this patch — observe that infinite scroll triggers as expected. --- ## ✅ Expected Behavior (After Fix) * Infinite scroll now consistently triggers when the anchor element enters the viewport or scroll container. * Works across different resolutions and container heights. * No duplicate observers created when dependencies change. --- ## 🧩 Code Example ```ts if (anchorRef.current && containerRef.current) { // Calculate dynamic rootMargin based on container height for better responsiveness const containerHeight = containerRef.current.clientHeight const dynamicMargin = Math.max(100, Math.min(containerHeight * 0.2, 200)) // Between 100px and 200px, or 20% of container height observer = new IntersectionObserver((entries) => { if (entries[0].isIntersecting && !isLoading && !error && hasMore) setSize((size: number) => size + 1) }, { root: containerRef.current || null, rootMargin: `${dynamicMargin}px`, threshold: 0.1, // Trigger when 10% of the anchor element is visible }) observer.observe(anchorRef.current) } return () => observer?.disconnect() }, [isLoading, setSize, anchorRef, data, error, containerRef]) ``` --- ## 🧰 Testing Notes * ✅ Verified on Chrome, Edge, and Safari. * ✅ Tested across resolutions: 720p, 1080p, 1440p, and 4K. * ✅ Confirmed no regression on normal scroll performance. * ✅ No console warnings or memory leaks observed. --- ## 🏷️ Type of Change | Type | Description | | -------------- | ------------------------------------------------------------ | | 🐛 Bug Fix | Fixes the infinite scroll not triggering on some resolutions | | 💅 Improvement | Adds dynamic margin and root handling for better UX | | 🧹 Refactor | Minor cleanup and code comments added | --- ## 🙌 Additional Notes This change does **not** alter backend logic or API interactions. It only improves frontend scroll event detection reliability. Reviewed and tested locally before submission.
yindo added the pull-request label 2026-02-21 20:50:09 -05:00
yindo closed this issue 2026-02-21 20:50:09 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langgenius/dify#31798