Files
suaveplan/packages/foundation/data/react-packer/src/provider/packed-resource-provider/packed-resource-provider.tsx
2026-05-27 23:47:53 +01:00

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;
}