@matthew.ngo/reform
Version:
A flexible and powerful React form management library with advanced validation, state observation, and multi-group support
339 lines (263 loc) • 11.2 kB
Markdown
# Reform Error Handling
Reform provides a comprehensive error handling system that helps you manage, format, and display form errors effectively. This document covers the error handling capabilities of Reform.
## Table of Contents
- [Overview](#overview)
- [Error Formatting](#error-formatting)
- [Error Boundaries](#error-boundaries)
- [Custom Error Renderers](#custom-error-renderers)
- [Error Transformation](#error-transformation)
- [API Reference](#api-reference)
## Overview
Reform's error handling system consists of several components:
1. **Error Formatting**: Customize how error messages are displayed
2. **Error Boundaries**: Catch and handle errors in form components
3. **Custom Error Renderers**: Create field-specific error displays
4. **Error Transformation**: Transform error messages before display
## Error Formatting
The `useErrorFormatter` hook provides utilities for formatting error messages with consistent styling and content.
### Basic Usage
```tsx
import { useReform, useErrorFormatter } from '@reform/core';
const MyForm = () => {
const form = useReform<UserForm>(config);
const errorFormatter = useErrorFormatter(form, {
format: 'html',
includeLabels: true,
});
// Format a field error
const formattedError = errorFormatter.formatFieldError(
'email',
'Invalid email format',
0 // group index
);
return (
<div>
{/* Display the formatted error */}
<div dangerouslySetInnerHTML={{ __html: formattedError.content }} />
</div>
);
};
```
### Configuration Options
The error formatter can be configured with the following options:
| Option | Type | Default | Description |
| ---------------- | ---------------------------- | --------- | ----------------------------------------------- |
| format | `'plain' \| 'html' \| 'jsx'` | `'plain'` | Format of error messages |
| includeLabels | `boolean` | `true` | Include field labels in error messages |
| includeRuleNames | `boolean` | `false` | Include validation rule names in error messages |
## Error Boundaries
Reform provides error boundary components to catch and handle errors in form components.
### FormErrorBoundary Component
The `FormErrorBoundary` component catches errors in its children and displays a fallback UI.
```tsx
import { useReform, FormErrorBoundary, RecoveryStrategy } from '@reform/core';
const MyForm = () => {
const form = useReform<UserForm>(config);
const groups = form.getGroups();
return (
<FormErrorBoundary
formGroups={groups}
onError={(error, errorInfo) => console.error(error)}
defaultRecoveryStrategy={RecoveryStrategy.RESET_FORM}
FallbackComponent={({ errorInfo, resetErrorBoundary, recover }) => (
<div>
<h2>Something went wrong!</h2>
<pre>{errorInfo.error.message}</pre>
<button onClick={() => recover(RecoveryStrategy.RESET_FORM)}>
Reset Form
</button>
</div>
)}
>
{/* Form components */}
</FormErrorBoundary>
);
};
```
### useFormErrorBoundary Hook
For a more convenient API, you can use the `useFormErrorBoundary` hook:
```tsx
import { useReform, useFormErrorBoundary } from '@reform/core';
const MyForm = () => {
const form = useReform<UserForm>(config);
const groups = form.getGroups();
const ErrorBoundary = useFormErrorBoundary(groups, {
onError: error => console.error(error),
FallbackComponent: MyCustomErrorComponent,
});
return <ErrorBoundary>{/* Form components */}</ErrorBoundary>;
};
```
### Recovery Strategies
Reform provides several recovery strategies for error boundaries:
| Strategy | Description |
| ----------------- | ----------------------------------------- |
| `RESET_FORM` | Reset the form to its initial state |
| `RETRY_OPERATION` | Retry the operation that caused the error |
| `IGNORE` | Ignore the error and continue |
| `CUSTOM` | Use a custom recovery function |
## Custom Error Renderers
Reform allows you to register custom error renderers for specific fields using the `useReformErrorRenderer` hook.
```tsx
import { useReform, useReformErrorRenderer } from '@reform/core';
import { useEffect } from 'react';
const MyForm = () => {
const form = useReform<UserForm>(config);
const errorRenderer = useReformErrorRenderer(form);
// Register a custom renderer for email fields
useEffect(() => {
const unregister = errorRenderer.registerErrorRenderer({
field: 'email',
renderer: ({ error }) => (
<div className="email-error">
<span className="icon">⚠️</span>
<span className="message">{error}</span>
</div>
),
});
return unregister;
}, []);
return (
<form>
<input {...form.register(0, 'email')} />
{/* Render the field error with the custom renderer */}
{errorRenderer.renderFieldError(0, 'email')}
</form>
);
};
```
## Error Transformation
You can transform error messages before they are displayed using the `registerTransformer` method of the `useErrorFormatter` hook.
```tsx
import { useReform, useErrorFormatter } from '@reform/core';
import { useEffect } from 'react';
const MyForm = () => {
const form = useReform<UserForm>(config);
const errorFormatter = useErrorFormatter(form);
// Register a custom transformer for email fields
useEffect(() => {
errorFormatter.registerTransformer('email', {
transform: (message, fieldPath, context) => {
// Add a prefix to all email error messages
return {
content: `Email Error: ${message}`,
format: 'html',
fieldPath,
meta: {
...context.group,
groupIndex: context.groupIndex,
},
};
},
});
}, []);
return <form>{/* Form fields */}</form>;
};
```
## API Reference
### useErrorFormatter
```typescript
function useErrorFormatter<T extends Record<string, any>>(
reform: ReformReturn<T>,
initialConfig?: Partial<ErrorFormatConfig>
): FormErrorFormatter<T>;
```
#### Parameters
| Parameter | Type | Description |
| ------------- | ---------------------------- | ---------------------------------- |
| reform | `ReformReturn<T>` | Reform hook return value |
| initialConfig | `Partial<ErrorFormatConfig>` | Initial error format configuration |
#### Returns
Returns a `FormErrorFormatter<T>` object with the following methods:
| Method | Description |
| ------------------- | ------------------------------------- |
| formatFieldError | Format a field error message |
| formatGroupError | Format a group-level error message |
| formatFormError | Format a form-level error message |
| registerTransformer | Register a custom error transformer |
| updateFormatConfig | Update the error format configuration |
### FormErrorBoundary
```typescript
class FormErrorBoundary<T extends Record<string, any>> extends React.Component<ErrorBoundaryProps<T>, ErrorBoundaryState<T>>
```
#### Props
| Prop | Type | Description |
| ----------------------- | ---------------------------------------------------- | ----------------------------------------------------- |
| children | `React.ReactNode` | Children to render |
| FallbackComponent | `React.ComponentType<ErrorFallbackProps<T>>` | Component to render when an error occurs |
| fallbackRender | `(props: ErrorFallbackProps<T>) => React.ReactNode` | Render prop alternative to FallbackComponent |
| fallback | `React.ReactNode` | Simple fallback UI as a React Element |
| onError | `(error: Error, errorInfo: React.ErrorInfo) => void` | Called when an error is caught |
| onReset | `() => void` | Called when the error boundary recovers from an error |
| defaultRecoveryStrategy | `RecoveryStrategy` | Default recovery strategy to apply |
| onCustomRecovery | `(errorInfo: ErrorInfo<T>) => void` | Custom recovery handler |
| formGroups | `FormGroup<T>[]` | Form groups to include in the error info |
### useFormErrorBoundary
```typescript
function useFormErrorBoundary<T extends Record<string, any>>(
formGroups: FormGroup<T>[],
options?: Omit<ErrorBoundaryProps<T>, 'children' | 'formGroups'>
): React.FC<{ children: React.ReactNode }>;
```
#### Parameters
| Parameter | Type | Description |
| ---------- | --------------------------------------------------------- | ---------------------------------------- |
| formGroups | `FormGroup<T>[]` | Form groups to include in the error info |
| options | `Omit<ErrorBoundaryProps<T>, 'children' \| 'formGroups'>` | Options for the error boundary |
#### Returns
Returns a React component that wraps its children in an error boundary.
### useReformErrorRenderer
```typescript
function useReformErrorRenderer<T extends Record<string, any>>(
reform: ReformReturn<T>
): {
registerErrorRenderer(config: ErrorRendererConfig<T>): () => void;
renderError(
field: FieldPath<T>,
error: string | null,
groupIndex?: number
): React.ReactNode;
renderFieldError(groupIndex: number, field: FieldPath<T>): React.ReactNode;
renderGroupErrors(groupIndex: number): React.ReactNode[];
};
```
#### Parameters
| Parameter | Type | Description |
| --------- | ----------------- | ------------------------ |
| reform | `ReformReturn<T>` | Reform hook return value |
#### Returns
Returns an object with methods for registering and using custom error renderers.
### ErrorTransformer
```typescript
interface ErrorTransformer<T> {
transform(
message: string,
fieldPath: FieldPath<T>,
context: {
group: FormGroup<T>;
groupIndex: number;
labels: Record<string, any>;
formatConfig: ErrorFormatConfig;
}
): FormattedErrorMessage;
}
```
### FormattedErrorMessage
```typescript
interface FormattedErrorMessage {
content: string;
format: 'plain' | 'html' | 'jsx';
fieldPath?: string;
fieldLabel?: string;
meta?: Record<string, any>;
}
```
### RecoveryStrategy
```typescript
enum RecoveryStrategy {
RESET_FORM = 'reset_form',
RETRY_OPERATION = 'retry_operation',
IGNORE = 'ignore',
CUSTOM = 'custom',
}
```