export function useLoadMore()

in packages/bonito-ui/src/hooks/use-load-more.ts [27:87]


export function useLoadMore<T>(
    onLoad: LoadMoreFn<T>,
    onLoadError?: (error: unknown) => void
) {
    const [items, setItems] = React.useState<T[]>([]);
    const [hasMore, setHasMore] = React.useState(true);
    const onLoadErrorRef = useRef(onLoadError);
    onLoadErrorRef.current = onLoadError;

    const pendingPromise = useRef<CancellablePromise<
        LoadMoreListResult<T>
    > | null>(null);

    const loadMore = useCallback(
        async (fresh: boolean = false): Promise<void> => {
            if (pendingPromise.current) {
                return;
            }
            pendingPromise.current = cancellablePromise(onLoad(fresh));
            try {
                const { items, done } = await pendingPromise.current;
                pendingPromise.current = null;
                if (!done && !items.length) {
                    // no more data, try again
                    return loadMore();
                }
                if (done) {
                    setHasMore(false);
                }
                setItems((oriItems) => [...oriItems, ...items]);
            } catch (error) {
                if (!(error instanceof CancelledPromiseError)) {
                    pendingPromise.current = null;
                    onLoadErrorRef.current?.(error);
                }
            }
        },
        [onLoad]
    );

    const loadFresh = useCallback(() => {
        if (pendingPromise.current) {
            pendingPromise.current.cancel();
            pendingPromise.current = null;
        }
        setItems([]);
        setHasMore(true);
        loadMore(true);
    }, [loadMore]);

    useEffect(() => {
        loadFresh();
    }, [loadFresh]);

    return {
        items,
        hasMore,
        onLoadMore: loadMore,
        onRefresh: loadFresh,
    };
}