import { Animated, RefreshControl } from "react-native"; import React, { useMemo } from "react"; import { RecyclerViewProps } from "../RecyclerViewProps"; import { getValidComponent, isComponentClass } from "../utils/componentUtils"; import { CompatView } from "../components/CompatView"; import { CompatAnimatedScroller } from "../components/CompatScroller"; import { getInvertedTransformStyle } from "../utils/getInvertedTransformStyle"; /** * Hook that manages secondary props and components for the RecyclerView. * This hook handles the creation and management of: * 1. Pull-to-refresh functionality * 2. Header and footer components * 3. Empty state component * 4. Custom scroll component with animation support * * @param props - The RecyclerViewProps containing all configuration options * @returns An object containing: * - refreshControl: The pull-to-refresh control component * - renderHeader: The header component renderer * - renderFooter: The footer component renderer * - renderEmpty: The empty state component renderer * - renderStickyHeaderBackdrop: The sticky header backdrop component renderer * - CompatScrollView: The animated scroll component */ export function useSecondaryProps(props: RecyclerViewProps) { const { ListHeaderComponent, ListHeaderComponentStyle, ListFooterComponent, ListFooterComponentStyle, ListEmptyComponent, ListEmptyComponentStyle, renderScrollComponent, refreshing, progressViewOffset, onRefresh, data, refreshControl: customRefreshControl, stickyHeaderConfig, inverted, horizontal, } = props; const invertedTransformStyle = inverted ? getInvertedTransformStyle(horizontal) : undefined; /** * Creates the refresh control component if onRefresh is provided. */ const refreshControl = useMemo(() => { if (customRefreshControl) { return customRefreshControl; } else if (onRefresh) { return ( ); } return undefined; }, [onRefresh, refreshing, progressViewOffset, customRefreshControl]); /** * Creates the header component with optional styling. */ const renderHeader = useMemo(() => { if (!ListHeaderComponent) { return null; } return ( {getValidComponent(ListHeaderComponent)} ); }, [ListHeaderComponent, ListHeaderComponentStyle, invertedTransformStyle]); /** * Creates the footer component with optional styling. */ const renderFooter = useMemo(() => { if (!ListFooterComponent) { return null; } return ( {getValidComponent(ListFooterComponent)} ); }, [ListFooterComponent, ListFooterComponentStyle, invertedTransformStyle]); /** * Creates the empty state component when there's no data. * Only rendered when ListEmptyComponent is provided and data is empty. */ const renderEmpty = useMemo(() => { if (!ListEmptyComponent || (data && data.length > 0)) { return null; } const emptyContent = getValidComponent(ListEmptyComponent); if (!invertedTransformStyle && !ListEmptyComponentStyle) { return emptyContent; } return ( {emptyContent} ); }, [ ListEmptyComponent, data, invertedTransformStyle, ListEmptyComponentStyle, ]); /** * Creates the sticky header backdrop component. */ const renderStickyHeaderBackdrop = useMemo(() => { if (!stickyHeaderConfig?.backdropComponent) { return null; } return ( {getValidComponent(stickyHeaderConfig?.backdropComponent)} ); }, [stickyHeaderConfig?.backdropComponent, invertedTransformStyle]); /** * Creates an animated scroll component based on the provided renderScrollComponent. * If no custom component is provided, uses the default CompatAnimatedScroller. */ const CompatScrollView = useMemo(() => { let scrollComponent: React.ComponentType = CompatAnimatedScroller; if ( typeof renderScrollComponent === "function" && !isComponentClass(renderScrollComponent) ) { // Create a forwarded ref wrapper for the custom scroll component const ForwardedScrollComponent = React.forwardRef((_props, ref) => (renderScrollComponent as (...args: unknown[]) => React.ReactNode)({ ..._props, ref, }) ); ForwardedScrollComponent.displayName = "CustomScrollView"; scrollComponent = ForwardedScrollComponent as React.ComponentType; } else if (renderScrollComponent) { scrollComponent = renderScrollComponent as React.ComponentType; } // Wrap the scroll component with Animated.createAnimatedComponent return Animated.createAnimatedComponent(scrollComponent); }, [renderScrollComponent]); return { refreshControl, renderHeader, renderFooter, renderEmpty, CompatScrollView, renderStickyHeaderBackdrop, }; }