react-native-use-in-view
Version:
A versatile and efficient React Native component designed for seamless integration of scroll-based features into mobile applications. This package offers a powerful ScrollView observer that allows developers to easily track and respond to scroll events an
100 lines • 5.08 kB
JavaScript
import React, { createContext, forwardRef, useCallback, useContext, useEffect, useRef, useState, } from 'react';
import { FlatList, ScrollView, } from 'react-native';
const ViewContext = createContext(null);
/**
* Hook to detect if an element is in the viewport.
* The `ref` must be attached to a native component (e.g., View, Text) or a custom component that forwards the ref to a native component.
*/
export const useInView = (options) => {
const { initialInView = false, threshold = 0, triggerOnce = false } = options || {};
const [inView, setInView] = useState(initialInView);
const elementRef = useRef(null);
const scrollContext = useContext(ViewContext);
const checkInView = useCallback(() => {
if (!elementRef.current?.measureInWindow)
return;
if (!scrollContext?.scrollViewRect) {
console.warn('useInView: Scroll context is not available. Ensure useInView is used within a ScrollViewObserver or FlatListObserver.');
return;
}
const { scrollViewRect } = scrollContext;
const { x: scrollViewX, y: scrollViewY, width: scrollViewWidth, height: scrollViewHeight, } = scrollViewRect;
elementRef.current.measureInWindow((x, y, width, height) => {
const elementRight = x + width;
const elementBottom = y + height;
const isVisible = elementRight > scrollViewX - threshold &&
elementBottom > scrollViewY - threshold &&
x < scrollViewX + scrollViewWidth + threshold &&
y < scrollViewY + scrollViewHeight + threshold;
if (isVisible && !inView)
setInView(true);
else if (!isVisible && !triggerOnce && inView)
setInView(false);
});
}, [inView, threshold]);
useEffect(() => {
const timeout = setTimeout(checkInView, 0);
return () => clearTimeout(timeout);
}, [checkInView]);
useEffect(() => {
if (scrollContext)
scrollContext.registerScrollListener(checkInView);
return () => {
if (scrollContext)
scrollContext.unregisterScrollListener(checkInView);
};
}, [scrollContext, checkInView]);
return { ref: elementRef, inView };
};
/**
* Generic hook to manage scroll listeners.
*/
const useScrollListeners = (options) => {
const listeners = useRef(new Set());
const internalRef = useRef(null);
const refToUse = (options.ref ?? internalRef);
const [scrollViewRect, setScrollViewRect] = useState({ x: 0, y: 0, width: 0, height: 0 });
const registerScrollListener = useCallback((listener) => {
listeners.current.add(listener);
}, []);
const unregisterScrollListener = useCallback((listener) => {
listeners.current.delete(listener);
}, []);
const handleScroll = useCallback((event) => {
listeners.current.forEach((listener) => listener());
}, []);
const handleLayout = (event) => {
setScrollViewRect(event.nativeEvent.layout);
if (options.onLayout)
options.onLayout(event);
};
return {
scrollRef: refToUse,
scrollViewRect,
registerScrollListener,
unregisterScrollListener,
handleScroll,
handleLayout,
};
};
/**
* Implementation of a ScrollView with in-view detection and scroll listeners.
* This component is used to wrap a ScrollView and provide in-view detection for its children.
* It takes all the props of a standard ScrollView and forwards the ref to the internal ScrollView.
*/
export const ScrollViewObserver = forwardRef(({ scrollEventThrottle = 100, onScroll, onLayout, ...rest }, ref) => {
const { scrollRef, scrollViewRect, registerScrollListener, unregisterScrollListener, handleScroll, handleLayout, } = useScrollListeners({ ref, onLayout });
return (React.createElement(ViewContext.Provider, { value: { registerScrollListener, unregisterScrollListener, scrollViewRect, scrollRef } },
React.createElement(ScrollView, { ref: scrollRef, onLayout: handleLayout, onScroll: handleScroll, scrollEventThrottle: scrollEventThrottle, ...rest })));
});
/**
* Implementation of a FlatList with in-view detection and scroll listeners.
* This component is used to wrap a FlatList and provide in-view detection for its children.
* It takes all the props of a standard FlatList and forwards the ref to the internal FlatList.
*/
export const FlatListObserver = ({ scrollEventThrottle = 100, flatListRef, onLayout, onScroll, ...rest }) => {
const { scrollRef, scrollViewRect, registerScrollListener, unregisterScrollListener, handleScroll, handleLayout, } = useScrollListeners({ ref: flatListRef, onLayout });
return (React.createElement(ViewContext.Provider, { value: { registerScrollListener, unregisterScrollListener, scrollViewRect, scrollRef } },
React.createElement(FlatList, { ref: scrollRef, onLayout: handleLayout, onScroll: handleScroll, scrollEventThrottle: scrollEventThrottle, ...rest })));
};
//# sourceMappingURL=index.js.map