3.2 KiB
useReactI18nRegistry & useOptionalReactI18nRegistry
Purpose
These hooks read the active ReactI18nRegistry from React context. The strict variant useReactI18nRegistry() throws REACT_I18N.REGISTRY_NOT_PROVIDED when called outside a <RegistryProvider> ancestor, which is the right behavior for code that genuinely requires the registry (e.g. plugin-aware components). The tolerant variant useOptionalReactI18nRegistry() returns null outside a provider, which is the right behavior for code that wants to opt into the registry when present but fall back to a non-registry implementation when absent — built-in hooks like useNumber use this so existing call sites without <RegistryProvider> keep working.
Features
- Strict and tolerant variants with the same context source.
- Stable error code (
REACT_I18N.REGISTRY_NOT_PROVIDED) on the strict variant for programmatic handling. - Zero overhead — both hooks are a single
useContextlookup.
Usage: Basic Example
import { useReactI18nRegistry } from "@suaveplan/react-i18n/registry";
function MyComponent() {
const registry = useReactI18nRegistry();
const formatter = registry.getFormatter<{ ms: number }, string>("duration");
return <span>{formatter?.format("en-GB", { ms: 1500 })}</span>;
}
Usage: Advanced Example
The tolerant variant is the right choice for any hook or component that should work both inside and outside a <RegistryProvider>. The built-in hooks (useNumber, useDate, …) use it internally:
import { useOptionalReactI18nRegistry } from "@suaveplan/react-i18n/registry";
function useNumber(value: number, options?: NumberOptions): string {
const registry = useOptionalReactI18nRegistry();
if (registry) {
const plugin = registry.getFormatter<NumberArgs, string>("number");
if (plugin) return plugin.format(useLocale(), { value, ...options });
}
// Fallback path — direct call to @suaveplan/i18n-formatter-number
return formatNumber(useLocale(), value, options);
}
For test fixtures that mount a registry per-case, the strict hook is appropriate — a missing provider in a test is a wiring bug and should fail loudly.
API Reference
useReactI18nRegistry(): ReactI18nRegistry
Reads the registry from RegistryContext. Throws ReactI18nError(REACT_I18N.REGISTRY_NOT_PROVIDED) when no <RegistryProvider> is in scope.
useOptionalReactI18nRegistry(): ReactI18nRegistry | null
Reads the registry from RegistryContext. Returns null when no <RegistryProvider> is in scope.
Implementation Notes
- Both hooks are bound to a single module-scoped
RegistryContextso the brand identity of the context is preserved across SSR / hot-reload / dev-bundle scenarios. - Two
<RegistryProvider>ancestors in the same tree result in the inner one winning — React context resolution is closest-ancestor wins. This is the desired behavior for layered apps where a sub-tree wants its own registry overlay; if you do not want that, mount only one provider. - The tolerant variant returns
nullrather than an "empty" registry sentinel. A null check at the call site is more honest than papering over the missing-provider case with a no-op registry.