react-granular-infinite-scroll v0.1.0
react granular infinite scroll
A React hook for implementing infinite scroll with granular control over the trigger point and fallback mechanism for fast scrolling scenarios.
Features
- šÆ Multiple ways to control scroll trigger points:
- Preset percentage-based triggers (50%, 75%, 100%)
- Automatic adaptation to growing list sizes
- š Built-in fallback mechanism for fast scrolling scenarios
- š Precise control over scroll trigger positioning
- āļø Customizable Intersection Observer options
- šļø Dynamic trigger points that adapt to list growth
- š·ļø Robust index tracking system
- š± Works with any scrollable container
- šØ Clean and simple API
Usage
import useFetchOnScroll from 'react-granular-infinite-scroll';;
const YourComponent = () => {
const fetchMore = () => {
// Your fetch logic here
};
const { ItemRef, fallbackRef, itemRefIndex, fallBackRefIndex } = useFetchOnScroll({
fetchNext: fetchMore,
numberOfItems: items.length,
triggerPoint: "75%", // Will be overridden if triggerIndex is provided
triggerIndex: Math.floor(items.length / 7) // Dynamic trigger point
});
return (
<div>
{items.map((item, index) => (
<div
key={item.id}
// data-id must be set to index for proper scroll tracking
data-id={index}
ref={index === itemRefIndex ? ItemRef : index === fallBackRefIndex ? fallbackRef : null}
>
{item.content}
</div>
))}
</div>
);
};
Important Implementation Notes
Required Data Attribute
The hook relies on the data-id
attribute to track item indexes during scrolling. When mapping through your items:
// ā
Correct implementation
{items.map((item, index) => (
<div
key={item.id}
data-id={index} // Required: Must be set to the item's index
ref={index === itemRefIndex ? ItemRef : index === fallBackRefIndex ? fallbackRef : null}
>
{item.content}
</div>
))}
// ā Incorrect - missing data-id
{items.map((item, index) => (
<div
key={item.id}
ref={index === itemRefIndex ? ItemRef : index === fallBackRefIndex ? fallbackRef : null}
>
{item.content}
</div>
))}
Usage
import useFetchOnScroll from 'react-granular-infinite-scroll';
const YourComponent = () => {
const fetchMore = () => {
// Your fetch logic here
};
const { ItemRef, fallbackRef, itemRefIndex, fallBackRefIndex } = useFetchOnScroll({
fetchNext: fetchMore,
numberOfItems: items.length,
triggerPoint: "75%", // Will be overridden if triggerIndex is provided
triggerIndex: Math.floor(items.length / 7) // Dynamic trigger point
});
return (
<div>
{items.map((item, index) => (
<div
key={item.id}
ref={index === itemRefIndex ? ItemRef : index === fallBackRefIndex ? fallbackRef : null}
data-id={index}
>
{item.content}
</div>
))}
</div>
);
};
Trigger Controls
Dynamic Index-based Triggers
The triggerIndex
prop offers superior granular control over scroll triggering by allowing dynamic index calculations. This enables you to:
- Create custom percentage-based triggers beyond the standard options:
// Trigger at exactly 33% of the list
useFetchOnScroll({
numberOfItems: items.length,
triggerIndex: Math.floor(items.length * 0.33)
});
- Implement dynamic trigger points that adapt to list size:
// Trigger point moves further down as list grows
useFetchOnScroll({
numberOfItems: items.length,
triggerIndex: Math.floor(items.length / 7) // Every 7th portion of the list
});
// Example behavior:
// - With 14 items: triggers at index 2
// - With 35 items: triggers at index 5
// - With 70 items: triggers at index 10
- Set precise trigger points based on business logic:
// Combining multiple factors
useFetchOnScroll({
numberOfItems: items.length,
triggerIndex: calculateTriggerIndex({ // Example of a business logic function
listLength: items.length,
viewportHeight: window.innerHeight,
itemHeight: 50
})
});
Priority and Override Behavior
ā ļø Important: When both triggerIndex
and triggerPoint
are provided, triggerIndex
always takes precedence and completely overrides the percentage-based triggerPoint
. This means:
// triggerPoint will be ignored, fetch triggers at dynamic index
useFetchOnScroll({
fetchNext: fetchMore,
numberOfItems: items.length,
triggerPoint: "75%", // Ignored
triggerIndex: Math.floor(items.length / 7) // Takes effect
});
Standard Percentage-based Triggers
If no triggerIndex
is provided, the hook uses percentage-based triggers through the triggerPoint
prop:
"50%"
: Triggers fetch when scrolling reaches halfway through the current items"75%"
: Triggers fetch at three-quarters through the list"100%"
: Traditional bottom-of-list trigger (default)
Why Fallback Ref?
The fallback reference system is a crucial feature that handles edge cases where users scroll very rapidly through the content. In such scenarios:
- The primary observer might miss the intersection event if the user scrolls too quickly past the trigger point
- The fallback ref, always placed at the last item, ensures that the fetch trigger isn't missed
- The hook maintains a session storage check to prevent duplicate fetches if both observers trigger
Performance Recommendations
Virtualization for Large Lists
When dealing with large lists (500+ items), it's strongly recommended to use this hook in combination with virtualization libraries:
import useFetchOnScroll from 'react-granular-infinite-scroll';
import { FixedSizeList } from 'react-window';
const VirtualizedList = () => {
const { ItemRef, fallbackRef, itemRefIndex, fallBackRefIndex } = useFetchOnScroll({
fetchNext: fetchMore,
numberOfItems: items.length,
triggerIndex: Math.floor(items.length / 7)
});
const Row = ({ index, style }) => (
<div
style={style}
data-id={index}
ref={index === itemRefIndex ? ItemRef : index === fallBackRefIndex ? fallbackRef : null}
>
{items[index].content}
</div>
);
return (
<FixedSizeList
height={500}
width="100%"
itemCount={items.length}
itemSize={50}
>
{Row}
</FixedSizeList>
);
};
Recommended virtualization libraries:
react-window
: Lightweight, modern virtualization libraryreact-virtualized
: Full-featured virtualization library with additional components
API
Props
Prop | Type | Default | Description |
---|---|---|---|
fetchNext | () => void | Required | Function to call when more items need to be fetched |
fetchMore | boolean | true | Flag to enable/disable fetch trigger |
numberOfItems | number | Required | Total number of current items |
threshold | number | undefined | Intersection Observer threshold |
rootMargin | string | undefined | Intersection Observer root margin |
triggerPoint | "50%" \| "75%" \| "100%" | "100%" | Point at which to trigger the fetch (ignored if triggerIndex is set) |
triggerIndex | number | undefined | Specific item index to trigger fetch. Supports dynamic calculations and overrides triggerPoint when set |
Returns
Value | Type | Description |
---|---|---|
ItemRef | RefCallback | Reference for the trigger point item |
fallbackRef | RefCallback | Reference for the fallback (last) item |
itemRefIndex | number | Index where the ItemRef should be placed |
fallBackRefIndex | number | Index where the fallbackRef should be placed |
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Support
If you encounter any issues or have questions, please file an issue on the GitHub repository.
5 months ago