import * as React from "react"; import { findNodeHandle } from "react-native"; import { useViewportEventsContext } from "../ViewportEvents"; import { callOnChildren } from "../utils"; export function ViewportTracker(props) { const viewportEvents = useViewportEventsContext(); const [nodeHandle, setNodeHandle] = React.useState(null); const childrenRef = React.useRef(props.children); const dimensionsRef = React.useRef({ width: 0, height: 0 }); const offsetRef = React.useRef({ offsetX: 0, offsetY: 0 }); const assignRef = React.useCallback((ref) => { childrenRef.current?.ref?.(ref); setNodeHandle(findNodeHandle(ref)); }, []); const onViewportChange = React.useCallback( ({ event = { ...dimensionsRef.current, ...offsetRef.current }, shouldMeasureLayout = true, } = {}) => { if (nodeHandle && event?.width > 0 && event?.height > 0) { viewportEvents?.notify?.({ parentHandle: nodeHandle, ...event, shouldMeasureLayout, }); } }, [nodeHandle, viewportEvents] ); const onLayout = React.useCallback( (event) => { callOnChildren(childrenRef.current, "onLayout", event); dimensionsRef.current = event?.nativeEvent?.layout; onViewportChange(); }, [onViewportChange] ); const onContentSizeChange = React.useCallback( (contentWidth, contentHeight) => { callOnChildren( childrenRef.current, "onContentSizeChange", contentWidth, contentHeight ); onViewportChange(); }, [onViewportChange] ); const onScroll = React.useCallback( (event) => { callOnChildren(childrenRef.current, "onScroll", event); offsetRef.current = event?.nativeEvent?.contentOffset; onViewportChange({ shouldMeasureLayout: false, }); }, [onViewportChange] ); React.useEffect(() => { childrenRef.current = props.children; }, [props.children]); return React.cloneElement(React.Children.only(props.children), { ref: assignRef, onLayout, onContentSizeChange, onScroll, }); }