Files
skills/docs/ui/react/react-ui/src/i18n/validation-message-bridge/validation-message-bridge.md
2026-05-29 08:20:56 +01:00

3.5 KiB

useValidationMessageBridge

(no submodules)

Purpose

Centralised resolver factory turning a SuaveplanError (or any thrown value) into a locale-resolved user-facing message. Mirrors the resolution rules of useErrorMessage from @suaveplan/react-i18n but returns a function — so a single hook call gives you a resolver you can apply to many errors per render: a list of validation errors on a form, a batch of toasts, a fan-out of notifications, or a status panel summarising recent failures. Every UI surface in react-ui (Toast, Alert, Dialog, ConfirmDeleteDialog) that needs to render an error should resolve through this bridge so the catalog-lookup behaviour stays consistent across the chassis.

Features

  • One hook call yields a resolver suitable for many errors.
  • Walks the cause chain to surface the deepest SuaveplanError.
  • Catalog lookup (errors:<code>) on hit; falls back to error.message on miss.
  • Coerces non-Error inputs (strings, undefined) without throwing.
  • Stable function reference across renders (memoized by useCallback).

Usage: Basic Example

import { useValidationMessageBridge } from "@suaveplan/react-ui";

function FormErrors({ errors }: { errors: SuaveplanError[] }) {
    const resolve = useValidationMessageBridge();
    return (
        <ul>
            {errors.map((e, i) => <li key={i}>{resolve(e)}</li>)}
        </ul>
    );
}

Usage: Advanced Example

Render a batch of validation errors inside an Alert with the bridge resolver:

function ValidationSummary({ errors }: { errors: unknown[] }) {
    const resolve = useValidationMessageBridge();
    return (
        <Alert variant="destructive">
            <AlertTitle>Please fix the following</AlertTitle>
            <AlertDescription>
                <ul>
                    {errors.map((err, i) => (
                        <li key={i}>{resolve(err)}</li>
                    ))}
                </ul>
            </AlertDescription>
        </Alert>
    );
}

A try/catch handler that surfaces a toast with the resolved message:

function useSubmit() {
    const resolve = useValidationMessageBridge();
    const { toast } = useToast();
    return useCallback(async (input: FormValues) => {
        try {
            await save(input);
        } catch (err) {
            toast({ variant: "destructive", description: resolve(err) });
        }
    }, [resolve, toast]);
}

API Reference

useValidationMessageBridge(): ValidationMessageResolver

Returns (error: unknown) => string. Resolution order:

  1. null / undefined → "".
  2. Walk cause chain → first SuaveplanError wins. Look up errors:<code>; on hit return the translation, on miss return the surfaced error's .message.
  3. Plain Error → error.message.
  4. string → itself.
  5. Anything else → "".

Implementation Notes

  • The hook reads the i18n instance via useI18n() and memoizes the resolver with useCallback([i18n]) so the function reference is stable as long as the i18n instance is. Languages-changed events bump i18n's snapshot counter; the bridge's resolver re-binds when that propagates so subsequent calls see the new locale's catalog.
  • Cause-chain walking is bounded at depth 10 to prevent runaway loops on accidentally-cyclic error structures.
  • The bridge does NOT perform any side effects (no logging, no telemetry). For metric emission on errors, wrap the call site or compose with @suaveplan/logger.