import type { EnsureFunction } from '@videojs/utils/types'; import type { AnyStore, InferStoreRequests } from '../../core/store'; import type { Task } from '../../core/task'; import type { MutationResult } from '../../shared/types'; import { useCallback, useRef, useSyncExternalStore } from 'react'; /** * Track a store request as a mutation with status, data, and error. * * Subscribes to the task queue and re-renders when the mutation's status changes. * * Returns a discriminated union — use `status` to narrow the type and access * `data` (on success) or `error` (on failure). * * @param store - The store instance containing the request * @param name - The request name to track (type-safe with autocomplete) * @returns A discriminated union with the mutation's current state * * @example * ```tsx * function SourceSelector() { * const source = useMutation(store, 'setSource'); * * return ( * <> * * {source.status === 'error' && ( *

Failed to load: {String(source.error)}

* )} * * ); * } * ``` */ export function useMutation< Store extends AnyStore, Name extends keyof InferStoreRequests, Mutate extends InferStoreRequests[Name] = InferStoreRequests[Name], >(store: Store, name: Name): MutationResult>>> { type Data = Awaited>>; const taskRef = useRef(store.queue.tasks[name]); const subscribe = useCallback( (onStoreChange: () => void) => store.queue.subscribe((tasks) => { const newTask = tasks[name]; if (newTask !== taskRef.current) { taskRef.current = newTask; onStoreChange(); } }), [store, name], ); const getSnapshot = useCallback(() => taskRef.current, []); const task = useSyncExternalStore(subscribe, getSnapshot, getSnapshot); const resetRef = useRef(() => store.queue.reset(name)); const base = { mutate: store.request[name] as Mutate, reset: resetRef.current, }; if (task?.status === 'success') { return { status: 'success', ...base, data: task.output as Data, }; } if (task?.status === 'error') { return { status: 'error', ...base, error: task.error, }; } return { status: task?.status ?? 'idle', ...base, } as MutationResult; } export namespace useMutation { export type Result = MutationResult; }