58 lines
1.8 KiB
TypeScript
58 lines
1.8 KiB
TypeScript
import { useContext } from "react";
|
|
import {
|
|
REACT_AUDIO_CODES,
|
|
ReactAudioError,
|
|
} from "../error-codes/error-codes.js";
|
|
import {
|
|
type AudioContextValue,
|
|
AudioReactContext,
|
|
type ProviderState,
|
|
} from "../provider/audio-context.js";
|
|
|
|
/**
|
|
* Internal helper: returns the resolved `ProviderState`, throwing
|
|
* `REACT_AUDIO.NO_PROVIDER` if rendered outside `<AudioProvider>`
|
|
* and `REACT_AUDIO.PROVIDER_PENDING` if the engine has not yet
|
|
* finished its async init.
|
|
*
|
|
* Implementation note: `useContext` is the only React hook called by
|
|
* this helper, so any downstream hook that uses it can register its
|
|
* own `useCallback`/`useMemo`/`useSyncExternalStore` hooks
|
|
* unconditionally without violating the Rules of Hooks. The throws
|
|
* happen *after* the context read, so the hook count is the same
|
|
* across the pending → ready transition.
|
|
*/
|
|
export function useProviderStateOrThrow(): ProviderState {
|
|
const ctx = useContext(AudioReactContext);
|
|
if (ctx === null) {
|
|
throw new ReactAudioError(
|
|
REACT_AUDIO_CODES.NO_PROVIDER,
|
|
"react-audio.no-provider"
|
|
);
|
|
}
|
|
if (ctx.state === null) {
|
|
throw new ReactAudioError(
|
|
REACT_AUDIO_CODES.PROVIDER_PENDING,
|
|
"react-audio.provider-pending",
|
|
{ context: { engineState: ctx.engineState } }
|
|
);
|
|
}
|
|
return ctx.state;
|
|
}
|
|
|
|
/**
|
|
* Internal helper: returns the raw context value (without throwing
|
|
* on pending), throwing `REACT_AUDIO.NO_PROVIDER` if the consumer
|
|
* is outside `<AudioProvider>`. Used by `useEngineState`.
|
|
*/
|
|
export function useAudioContextOrThrow(): AudioContextValue {
|
|
const ctx = useContext(AudioReactContext);
|
|
if (ctx === null) {
|
|
throw new ReactAudioError(
|
|
REACT_AUDIO_CODES.NO_PROVIDER,
|
|
"react-audio.no-provider"
|
|
);
|
|
}
|
|
return ctx;
|
|
}
|