70 lines
2.4 KiB
TypeScript
70 lines
2.4 KiB
TypeScript
import { packedResourceManager as defaultSingleton } from "@suaveplan/packer-browser/singleton";
|
|
import type { MessageResolver } from "@suaveplan/types";
|
|
import {
|
|
createContext,
|
|
type ReactElement,
|
|
type ReactNode,
|
|
useContext,
|
|
useMemo,
|
|
} from "react";
|
|
import type {
|
|
PackedResourceManager,
|
|
PackedResourceProviderValue,
|
|
} from "../../types/react-packer.types.js";
|
|
|
|
/**
|
|
* React context that carries a `PackedResourceManager` (and optionally a
|
|
* `MessageResolver`) down the tree to every `usePackedResource*` hook.
|
|
* The context default is `null` — hooks then resolve the manager via
|
|
* the documented singleton fallback so consumers who don't need DI get
|
|
* a zero-config experience.
|
|
*/
|
|
const PackedResourceContext = createContext<PackedResourceProviderValue | null>(
|
|
null
|
|
);
|
|
|
|
/**
|
|
* Mount this provider once at the root of any React subtree that wants
|
|
* to share a `PackedResourceManager` across hooks. Mount multiple
|
|
* providers if different subtrees need different managers — child
|
|
* providers shadow parents normally.
|
|
*/
|
|
export function PackedResourceProvider(props: {
|
|
readonly value: PackedResourceProviderValue;
|
|
readonly children?: ReactNode;
|
|
}): ReactElement {
|
|
// Memoise so child consumers don't re-render when the parent
|
|
// re-renders without changing the manager identity.
|
|
const value = useMemo(
|
|
() => props.value,
|
|
[props.value.manager, props.value.messageResolver, props.value]
|
|
);
|
|
return (
|
|
<PackedResourceContext.Provider value={value}>
|
|
{props.children}
|
|
</PackedResourceContext.Provider>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Resolve the active `PackedResourceManager`. Returns the provider's
|
|
* manager when one is mounted; otherwise returns the module-singleton
|
|
* exported by `@suaveplan/packer-browser/singleton`.
|
|
*/
|
|
export function usePackedResourceManager(): PackedResourceManager {
|
|
const ctx = useContext(PackedResourceContext);
|
|
if (ctx !== null) return ctx.manager;
|
|
return defaultSingleton;
|
|
}
|
|
|
|
/**
|
|
* Resolve the active `MessageResolver`, if any. Returns the
|
|
* provider-supplied resolver when one is mounted; otherwise returns
|
|
* `undefined` so the underlying error class falls through to its
|
|
* default message-key behaviour.
|
|
*/
|
|
export function usePackedMessageResolver(): MessageResolver | undefined {
|
|
const ctx = useContext(PackedResourceContext);
|
|
return ctx?.messageResolver;
|
|
}
|