import React, { useCallback, useEffect, useRef } from "react"; import { Animated, Easing, Platform, View } from "react-native"; type ViewProps = React.ComponentProps; export type IndicatorProps = ViewProps & { animationEasing?: typeof Easing.linear; animationDuration?: number; count?: number; renderComponent?: (props: any) => JSX.Element; }; /** A component used to provide a visual cue that an action is either processing, awaiting a course of change or a result. */ const Indicator = React.memo( React.forwardRef( ( { animationEasing = Easing.linear, animationDuration = 1200, renderComponent = () => { return <>; }, count = 1, ...rest }: IndicatorProps, ref: any ) => { const progress = useRef(new Animated.Value(0)).current; const startAnimation = useCallback(() => { let animation = Animated.timing(progress, { duration: animationDuration, easing: animationEasing, useNativeDriver: Platform.OS !== "web", // Web doesn't support loops if using Native Driver, isInteraction: true, toValue: 1, }); Animated.loop(animation).start(); }, [animationDuration, animationEasing, progress]); const _renderComponent = useCallback( (item: number, index: number) => { if ("function" === typeof renderComponent) { return renderComponent({ index, count, progress }); } return null; }, [renderComponent, count, progress] ); useEffect(() => { startAnimation(); }, []); return ( <>{Array.from(new Array(count), _renderComponent, this)} ); } ) ); export default Indicator;