import * as React from "react"; import * as R from "ramda"; type SequentialLoadingContextType = { isLoaded: (index: number) => boolean; onLoadFinished: (index: number) => (any) => void; allLoaded: boolean; isReadyToLoad: (index: number) => boolean; } | null; type SequentialLoadingProviderTypes = { children: JSX.Element; data: Array; }; export const SequentialLoadingContext = React.createContext(null); export const SequentialLoadingProvider = ({ children, data, }: SequentialLoadingProviderTypes) => { const initialData = React.useMemo(() => { const arr = new Array(data.length).fill(false); return R.set(R.lensIndex(0), true)(arr); }, []); const [componentsState, dispatch] = React.useReducer( (state, { data, type }) => { switch (type) { case "set": return R.set(R.lensIndex(data), true)(state); case "reset": return data; default: return state; } }, initialData ); const componentsStateRef = React.useRef(componentsState); React.useLayoutEffect(() => { componentsStateRef.current = componentsState; }, [componentsState]); const onLoadFinished = React.useCallback( (index) => () => { dispatch({ data: index, type: "set" }); }, [] ); const isLoaded = React.useCallback((index) => { return componentsStateRef.current[index]; }, []); const isReadyToLoad = React.useCallback( (index) => R.compose(R.all(R.equals(true)), R.take(index))(componentsState), [componentsState] ); const allLoaded = R.all(R.equals(true), componentsState); return ( {children} ); }; export const useSequentialLoader = () => React.useContext(SequentialLoadingContext);