UNPKG

@assistant-ui/react

Version:

TypeScript/React library for AI Chat

68 lines (60 loc) 1.82 kB
import { useCallback, useRef, useState } from "react"; import { useLatestRef } from "./useLatestRef"; export type RunManager = Readonly<{ isRunning: boolean; schedule: () => void; cancel: () => void; }>; export function useRunManager(config: { onRun: (signal: AbortSignal) => Promise<void>; onFinish?: (() => void) | undefined; onError?: ((error: Error) => void) | undefined; }): RunManager { const [isRunning, setIsRunning] = useState(false); const stateRef = useRef({ pending: false, abortController: null as AbortController | null, }); const onRunRef = useLatestRef(config.onRun); const onFinishRef = useLatestRef(config.onFinish); const onErrorRef = useLatestRef(config.onError); const startRun = useCallback(() => { setIsRunning(true); stateRef.current.pending = false; const ac = new AbortController(); stateRef.current.abortController = ac; queueMicrotask(async () => { try { await onRunRef.current(ac.signal); } catch (error) { stateRef.current.pending = false; onErrorRef.current?.(error as Error); } finally { onFinishRef.current?.(); if (stateRef.current.pending) { startRun(); } else { setIsRunning(false); stateRef.current.abortController = null; } } }); }, [onRunRef, onFinishRef]); const schedule = useCallback(() => { if (stateRef.current.abortController) { // Coalesce multiple schedules while running into a single follow-up run. stateRef.current.pending = true; return; } startRun(); }, [startRun]); const cancel = useCallback(() => { stateRef.current.pending = false; stateRef.current.abortController?.abort(); }, []); return { isRunning, schedule, cancel, }; }