# Reform Validation

Reform provides a powerful validation system that helps you validate form data with multiple strategies. This document covers the validation capabilities of Reform.

## Table of Contents

- [Overview](#overview)
- [Yup Integration](#yup-integration)
- [Dynamic Validation](#dynamic-validation)
- [Context-Aware Validation](#context-aware-validation)
- [Field-Level Validation](#field-level-validation)
- [Group Validation](#group-validation)
- [Form Submission Validation](#form-submission-validation)
- [API Reference](#api-reference)

## Overview

Reform's validation system offers several validation strategies:

1. **Schema-based validation** with Yup
2. **Dynamic validation rules** based on field values
3. **Context-aware validation** that can access other form data
4. **Field-level validation** for individual fields
5. **Group validation** for validating entire form groups
6. **Form submission validation** for pre-submission checks

## Yup Integration

Reform provides seamless integration with Yup for schema-based validation.

### Basic Usage

```tsx
import { useReform } from '@reform/core';
import * as yup from 'yup';

// Define your validation schema
const userSchema = yup.object({
  name: yup.string().required('Name is required'),
  email: yup.string().email('Invalid email').required('Email is required'),
  age: yup.number().min(18, 'Must be at least 18 years old'),
});

const MyForm = () => {
  const form = useReform<UserForm>({
    defaultData: { name: '', email: '', age: 0 },
    minGroups: 1,
    groupSchema: userSchema,
  });

  return (
    <form onSubmit={form.formMethods.handleSubmit(onSubmit)}>
      {/* Form fields */}
    </form>
  );
};
```

### Enhanced Yup Schema

Reform extends Yup's functionality with transformers and context:

```tsx
import { useReform } from '@reform/core';
import * as yup from 'yup';

const MyForm = () => {
  const form = useReform<UserForm>(config);
  
  // Create an enhanced schema with transformers and context
  const enhancedSchema = form.yup.createEnhancedSchema(
    yup.object({
      birthDate: yup.date().required('Birth date is required'),
      registrationDate: yup.date().min(
        yup.ref('birthDate'),
        'Registration date must be after birth date'
      ),
    })
  );
  
  // Register a transformer for date fields
  useEffect(() => {
    const unregister = form.yup.registerTransformer({
      field: 'birthDate',
      transformer: (value) => value instanceof Date ? value : new Date(value),
      transformOn: 'input'
    });
    
    return unregister;
  }, []);
  
  return (
    <form>
      {/* Form fields */}
    </form>
  );
};
```

## Dynamic Validation

Reform allows you to create dynamic validation rules that change based on form values.

```tsx
import { useReform, useDynamicValidation } from '@reform/core';

const MyForm = () => {
  const form = useReform<UserForm>(config);
  
  const dynamicValidation = useDynamicValidation(form, {
    rules: [
      {
        field: 'password',
        validate: (value, data) => {
          if (!value) return 'Password is required';
          if (value.length < 8) return 'Password must be at least 8 characters';
          return null;
        },
      },
      {
        field: 'confirmPassword',
        validate: (value, data, context) => {
          if (value !== data.password) {
            return 'Passwords do not match';
          }
          return null;
        },
        dependencies: ['password'],
      },
    ],
  });
  
  return (
    <form>
      {/* Form fields */}
    </form>
  );
};
```

## Context-Aware Validation

Reform's validation system can access context data from other parts of the form:

```tsx
import { useReform } from '@reform/core';
import * as yup from 'yup';

const MyForm = () => {
  const form = useReform<UserForm>(config);
  
  // Update context data
  useEffect(() => {
    form.yup.updateContextData({
      minAge: 18,
      maxAge: 65,
      currentDate: new Date(),
    });
  }, []);
  
  // Create a schema that uses context
  const schema = form.yup.createSchemaWithContext(
    yup.object({
      age: yup.number().test(
        'age-range',
        'Age must be between ${context.minAge} and ${context.maxAge}',
        function(value) {
          const { minAge, maxAge } = this.options.context;
          return value >= minAge && value <= maxAge;
        }
      ),
    })
  );
  
  return (
    <form>
      {/* Form fields */}
    </form>
  );
};
```

## Field-Level Validation

Reform supports field-level validation through React Hook Form's validation rules:

```tsx
import { useReform } from '@reform/core';

const MyForm = () => {
  const form = useReform<UserForm>(config);
  
  return (
    <form>
      <input
        {...form.register(0, 'email', {
          required: 'Email is required',
          pattern: {
            value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
            message: 'Invalid email address',
          },
        })}
        placeholder="Email"
      />
      {form.getFieldError(0, 'email') && (
        <p>{form.getFieldError(0, 'email')}</p>
      )}
    </form>
  );
};
```

## Group Validation

Reform provides utilities to validate entire form groups and check if they meet specific requirements:

```tsx
import { useReform } from '@reform/core';

const MyForm = () => {
  const form = useReform<UserForm>(config);
  const { isGroupValid, areAllGroupsValid } = form.errors;
  
  // Define required fields for validation
  const requiredFields: (keyof UserForm)[] = ['name', 'email', 'age'];
  
  // Check if a specific group is valid
  const isFirstGroupValid = isGroupValid(0, requiredFields);
  
  // Check if all groups are valid
  const allGroupsValid = areAllGroupsValid(requiredFields);
  
  const handleAddGroup = () => {
    // Only add a new group if all existing groups are valid
    if (allGroupsValid) {
      form.addGroup();
    } else {
      alert('Please complete all required fields in existing groups first');
    }
  };
  
  return (
    <form>
      {/* Form fields */}
      <button 
        type="button" 
        onClick={handleAddGroup}
        disabled={!allGroupsValid}
      >
        Add Group
      </button>
    </form>
  );
};
```

## Form Submission Validation

Reform provides utilities to check if a form is ready for submission:

```tsx
import { useReform } from '@reform/core';

const MyForm = () => {
  const form = useReform<UserForm>(config);
  const { isFormSubmittable } = form.errors;
  
  // Define required fields for submission
  const requiredFields: (keyof UserForm)[] = ['name', 'email', 'age'];
  
  // Check if the form can be submitted
  const canSubmit = isFormSubmittable(requiredFields);
  
  const handleSubmit = (data: any) => {
    console.log('Form submitted:', data);
  };
  
  return (
    <form onSubmit={form.formMethods.handleSubmit(handleSubmit)}>
      {/* Form fields */}
      <button 
        type="submit" 
        disabled={!canSubmit}
      >
        Submit
      </button>
      
      {!canSubmit && (
        <p className="error">
          Please complete all required fields and fix any errors before submitting
        </p>
      )}
    </form>
  );
};
```

## API Reference

### useReformYupIntegration

```typescript
function useReformYupIntegration<T extends Record<string, any> & AnyObject>(
  reform: ReformReturn<T>
): ReformYupIntegration<T>
```

#### Returns

Returns a `ReformYupIntegration<T>` object with the following methods:

| Method | Description |
|--------|-------------|
| createEnhancedSchema | Create a schema with transformers and context |
| validateWithContext | Validate data with context for a specific group |
| updateContextData | Update context data for both context and transformers |
| validateAllGroups | Validate all groups with the current schema |
| createSchemaWithContext | Create a schema with context support |
| createSchemaWithTransformers | Create a schema with transformer support |
| registerTransformer | Register a transformer for a specific field |
| unregisterTransformer | Unregister a transformer |

### useDynamicValidation

```typescript
function useDynamicValidation<T extends Record<string, any>>(
  reform: ReformReturn<T>,
  config: DynamicSchemaConfig<T>
): {
  validateField(groupIndex: number, field: FieldPath<T>, value: any): ValidationRuleResult;
  validateGroup(groupIndex: number): boolean;
  getFieldRules(groupIndex: number, field: FieldPath<T>): DynamicValidationRule<T>[];
  updateContext(newContextData: Record<string, any>): void;
}
```

#### Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| reform | `ReformReturn<T>` | Reform hook return value |
| config | `DynamicSchemaConfig<T>` | Configuration for dynamic validation |

#### Configuration Options

| Option | Type | Description |
|--------|------|-------------|
| rules | `DynamicValidationRule<T>[]` | Array of validation rules |
| context | `Record<string, any>` | Context data for validation |

### FormErrorsState

```typescript
interface FormErrorsState<T extends Record<string, any>> {
  errors: Record<string, FieldError>;
  hasErrors: boolean;
  getFieldError: (groupIndex: number, fieldName: string) => string | undefined;
  getGroupErrors: (index: number) => Record<string, string>;
  getFieldErrors: (index: number) => Record<keyof T, string | undefined>;
  clearErrors: () => void;
  clearFieldError: (groupIndex: number, fieldName: string) => void;
  clearGroupError: (groupIndex: number) => void;
  clearGlobalError: () => void;
  getError: (groupIndex: number, field: string) => string | undefined;
  hasGroupErrors: (groupIndex: number) => boolean;
  isGroupValid: (groupIndex: number, requiredFields: (keyof T)[]) => boolean;
  areAllGroupsValid: (requiredFields: (keyof T)[]) => boolean;
  isFormSubmittable: (requiredFields: (keyof T)[]) => boolean;
  canAddNewGroup: (requiredFields: (keyof T)[]) => boolean;
}
```

### DynamicValidationRule

```typescript
interface DynamicValidationRule<T> {
  field: FieldPath<T>;
  validate: (
    value: any,
    data: T,
    context: ValidationContext
  ) => string | null | Promise<string | null>;
  dependencies?: Array<FieldPath<T>>;
  runCondition?: (data: T, context: ValidationContext) => boolean;
}
```

### YupTransformer

```typescript
interface YupTransformer<T> {
  field: FieldPath<T>;
  transformer: (value: any) => any;
  transformOn: 'input' | 'output' | 'both';
}
```
