# Validation

## Overview

The `@ic-reactor/react` package provides utilities to easily handle validation errors thrown by `DisplayReactor`. These helpers make it simple to integrate with form libraries like React Hook Form or manage state manually.

## Import

```typescript
import {
  mapValidationErrors,
  getFieldError,
  handleValidationError,
  isValidationError,
} from "@ic-reactor/react"
```

## Utilities

### mapValidationErrors

Maps a `ValidationError` to a simple object where keys are field names and values are error messages.

```typescript
function mapValidationErrors(
  error: ValidationError,
  options?: { multiple?: boolean }
): Record<string, string | string[]>
```

**Usage:**

```tsx
try {
  await mutate([data])
} catch (error) {
  if (isValidationError(error)) {
    const errors = mapValidationErrors(error)
    console.log(errors) // { to: "Required", amount: "Must be positive" }
    setFormErrors(errors)
  }
}
```

### handleValidationError

Creates an error handler callback that automatically checks for `ValidationError` and extracts field errors. Perfect for `onError` callbacks.

```typescript
function handleValidationError(
  setFieldErrors: (errors: Record<string, string>) => void,
  onOtherError?: (error: Error) => void
): (error: Error) => void
```

**Usage:**

```tsx
const { mutate } = useReactorMutation({
  reactor: backend,
  functionName: "transfer",
  onError: handleValidationError(
    (errors) => setFormErrors(errors), // Called if validation error
    (error) => toast.error(error.message) // Called for other errors
  ),
})
```

### getFieldError

Extracts the first error message for a specific field.

```typescript
function getFieldError(
  error: ValidationError,
  field: string
): string | undefined
```

### extractValidationErrors

A type-safe helper that returns the mapped errors if the error is a `ValidationError`, or `null` otherwise.

```typescript
function extractValidationErrors(error: unknown): Record<string, string> | null
```

## integration Examples

```tsx
import { useState } from "react"
import {
  useReactorMutation,
  mapValidationErrors,
  isValidationError,
} from "@ic-reactor/react"
import { backend } from "./reactor"

function TransferForm() {
  const [errors, setErrors] = useState<Record<string, string>>({})

  const { mutate, isPending } = useReactorMutation({
    reactor: backend,
    functionName: "transfer",
    onError: (error) => {
      if (isValidationError(error)) {
        setErrors(mapValidationErrors(error))
      }
    },
  })

  const handleSubmit = (e) => {
    e.preventDefault()
    const formData = new FormData(e.target)
    mutate([
      {
        to: formData.get("to") as string,
        amount: formData.get("amount") as string,
      },
    ])
  }

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <input name="to" placeholder="To" />
        {errors.to && <span className="error">{errors.to}</span>}
      </div>

      <div>
        <input name="amount" placeholder="Amount" />
        {errors.amount && <span className="error">{errors.amount}</span>}
      </div>

      <button disabled={isPending}>Submit</button>
    </form>
  )
}
```

```tsx
import { useForm } from "react-hook-form"
import {
  useReactorMutation,
  mapValidationErrors,
  isValidationError,
} from "@ic-reactor/react"
import { backend } from "./reactor"

function TransferForm() {
  const { register, handleSubmit, setError } = useForm()

  const { mutate } = useReactorMutation({
    reactor: backend,
    functionName: "transfer",
    onError: (error) => {
      if (isValidationError(error)) {
        const fieldErrors = mapValidationErrors(error)
        Object.entries(fieldErrors).forEach(([field, message]) => {
          setError(field, { type: "server", message })
        })
      }
    },
  })

  const onSubmit = (data) => mutate([data])

  return <form onSubmit={handleSubmit(onSubmit)}>{/* Inputs... */}</form>
}
```