# Reform Form Persistence

Reform provides a powerful form persistence system that allows you to save and restore form state across sessions. This document covers the form persistence capabilities of Reform.

## Table of Contents

- [Overview](#overview)
- [Basic Usage](#basic-usage)
- [Auto-Save](#auto-save)
- [Custom Storage Providers](#custom-storage-providers)
- [Callbacks and Metadata](#callbacks-and-metadata)
- [API Reference](#api-reference)

## Overview

Reform's form persistence system allows you to:

1. **Save form state** to localStorage or a custom storage provider
2. **Restore form state** when the user returns to the form
3. **Auto-save** form changes at regular intervals
4. **Add metadata** to saved form state for additional context

## Basic Usage

Here's a basic example of using form persistence:

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

type UserForm = {
  name: string;
  email: string;
  preferences: {
    theme: string;
    notifications: boolean;
  };
};

const UserFormComponent = () => {
  const form = useReform<UserForm>({
    defaultData: {
      name: '',
      email: '',
      preferences: {
        theme: 'light',
        notifications: true,
      },
    },
    minGroups: 1,
  });

  // Set up form persistence
  const persistence = useReformPersistence(form, {
    enabled: true,
    storageKey: 'user-form-state',
  });

  // Manual save and restore
  const handleSave = async () => {
    await persistence.saveState();
    alert('Form state saved!');
  };

  const handleRestore = async () => {
    await persistence.loadState();
    alert('Form state restored!');
  };

  const handleClear = async () => {
    await persistence.clearState();
    alert('Saved form state cleared!');
  };

  return (
    <div>
      <form>
        <div>
          <label>Name</label>
          <input {...form.register(0, 'name')} />
        </div>
        <div>
          <label>Email</label>
          <input {...form.register(0, 'email')} />
        </div>
        <div>
          <label>Theme</label>
          <select {...form.register(0, 'preferences.theme')}>
            <option value="light">Light</option>
            <option value="dark">Dark</option>
            <option value="system">System</option>
          </select>
        </div>
        <div>
          <label>
            <input
              type="checkbox"
              {...form.register(0, 'preferences.notifications')}
            />
            Enable notifications
          </label>
        </div>
      </form>

      <div className="persistence-controls">
        <button onClick={handleSave}>Save Form State</button>
        <button onClick={handleRestore}>Restore Form State</button>
        <button onClick={handleClear}>Clear Saved State</button>
        
        {persistence.lastSaved && (
          <div className="last-saved">
            Last saved: {new Date(persistence.lastSaved).toLocaleString()}
          </div>
        )}
      </div>
    </div>
  );
};
```

## Auto-Save

Reform can automatically save form state at regular intervals or when the form changes:

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

const AutoSaveForm = () => {
  const form = useReform<UserForm>(config);
  
  // Set up auto-save
  const persistence = useReformPersistence(form, {
    enabled: true,
    storageKey: 'auto-save-form',
    autoSave: true,
    autoSaveInterval: 10000, // Save every 10 seconds
    debounceAutoSave: true,  // Debounce saves when form changes
    debounceDelay: 1000,     // Wait 1 second after changes before saving
  });
  
  // Toggle auto-save
  const toggleAutoSave = () => {
    persistence.setAutoSave(!persistence.isAutoSaveEnabled);
  };
  
  return (
    <div>
      <form>{/* Form fields */}</form>
      
      <div className="auto-save-controls">
        <label>
          <input
            type="checkbox"
            checked={persistence.isAutoSaveEnabled}
            onChange={toggleAutoSave}
          />
          Enable auto-save
        </label>
        
        <button onClick={persistence.resetAutoSaveTimer}>
          Reset Auto-Save Timer
        </button>
        
        {persistence.lastSaved && (
          <div className="last-saved">
            Last auto-saved: {new Date(persistence.lastSaved).toLocaleString()}
          </div>
        )}
      </div>
    </div>
  );
};
```

## Custom Storage Providers

You can create custom storage providers to save form state to different locations:

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

// Create a custom storage provider that uses IndexedDB
class IndexedDBStorageProvider<T> implements StorageProvider<T> {
  private dbName: string;
  private storeName: string;

  constructor(dbName: string = 'reformForms', storeName: string = 'formState') {
    this.dbName = dbName;
    this.storeName = storeName;
  }

  private async getDB(): Promise<IDBDatabase> {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.dbName, 1);
      
      request.onupgradeneeded = () => {
        const db = request.result;
        if (!db.objectStoreNames.contains(this.storeName)) {
          db.createObjectStore(this.storeName);
        }
      };
      
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async save(key: string, data: any): Promise<void> {
    const db = await this.getDB();
    return new Promise((resolve, reject) => {
      const transaction = db.transaction(this.storeName, 'readwrite');
      const store = transaction.objectStore(this.storeName);
      const request = store.put(data, key);
      
      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }

  async load(key: string): Promise<any> {
    const db = await this.getDB();
    return new Promise((resolve, reject) => {
      const transaction = db.transaction(this.storeName, 'readonly');
      const store = transaction.objectStore(this.storeName);
      const request = store.get(key);
      
      request.onsuccess = () => resolve(request.result || null);
      request.onerror = () => reject(request.error);
    });
  }

  async remove(key: string): Promise<void> {
    const db = await this.getDB();
    return new Promise((resolve, reject) => {
      const transaction = db.transaction(this.storeName, 'readwrite');
      const store = transaction.objectStore(this.storeName);
      const request = store.delete(key);
      
      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }
}

// Use the custom storage provider
const MyForm = () => {
  const form = useReform<UserForm>(config);
  
  const persistence = useReformPersistence(form, {
    enabled: true,
    storageKey: 'indexed-db-form',
    storageProvider: new IndexedDBStorageProvider(),
  });
  
  return (
    <div>{/* Form fields */}</div>
  );
};
```

## Callbacks and Metadata

Reform allows you to add callbacks and metadata to form persistence:

```tsx
import { useReform, useReformPersistence } from '@reform/core';
import { useAuth } from './auth';

const FormWithMetadata = () => {
  const form = useReform<UserForm>(config);
  const { user } = useAuth();
  
  const persistence = useReformPersistence(form, {
    enabled: true,
    storageKey: `user-form-${user.id}`,
    
    // Add metadata to saved form state
    metadata: {
      userId: user.id,
      formVersion: '1.2.0',
      lastModifiedBy: user.email,
    },
    
    // Or use a function to get dynamic metadata
    // metadata: () => ({
    //   userId: user.id,
    //   timestamp: new Date().toISOString(),
    //   browser: navigator.userAgent,
    // }),
    
    // Callbacks
    onBeforeSave: (groups) => {
      // Return false to prevent saving
      if (groups.some(group => !group.data.name)) {
        alert('Please fill in all name fields before saving');
        return false;
      }
      return true;
    },
    
    onAfterSave: (groups) => {
      console.log('Form saved successfully', groups);
    },
    
    onAfterRestore: (groups) => {
      console.log('Form restored successfully', groups);
    },
  });
  
  return (
    <div>{/* Form fields */}</div>
  );
};
```

## API Reference

### useReformPersistence

```typescript
function useReformPersistence<T extends Record<string, any>>(
  reform: ReformReturn<T>,
  config: FormPersistenceConfig<T> = {}
): FormPersistenceReturn<T>
```

#### Configuration Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| enabled | `boolean` | `false` | Enable/disable persistence |
| storageKey | `string` | `"reform-form-state"` | Key for storing form state |
| storageProvider | `StorageProvider<T>` | `localStorage` | Storage provider |
| autoSave | `boolean` | `false` | Enable auto-saving |
| autoSaveInterval | `number` | `5000` | Auto-save interval in ms |
| debounceAutoSave | `boolean` | `true` | Debounce auto-save |
| debounceDelay | `number` | `500` | Debounce delay in ms |
| onBeforeSave | `(groups: FormGroup<T>[]) => boolean \| Promise<boolean>` | `undefined` | Hook before saving |
| onAfterSave | `(groups: FormGroup<T>[]) => void` | `undefined` | Hook after saving |
| onAfterRestore | `(groups: FormGroup<T>[]) => void` | `undefined` | Hook after restoring |
| metadata | `Record<string, any> \| () => Record<string, any>` | `undefined` | Additional metadata |

#### Return Value

| Property/Method | Type | Description |
|----------------|------|-------------|
| isSaving | `boolean` | Whether form is currently saving |
| isLoading | `boolean` | Whether form is currently loading |
| lastSaved | `number \| null` | Timestamp of last save |
| saveState | `() => Promise<void>` | Save form state manually |
| loadState | `() => Promise<void>` | Load form state manually |
| clearState | `() => Promise<void>` | Clear saved form state |
| resetAutoSaveTimer | `() => void` | Reset auto-save timer |
| setAutoSave | `(enabled: boolean) => void` | Enable/disable auto-save |
| isAutoSaveEnabled | `boolean` | Whether auto-save is enabled |

### StorageProvider

```typescript
interface StorageProvider<T> {
  save: (key: string, data: FormPersistenceData<T>) => Promise<void>;
  load: (key: string) => Promise<FormPersistenceData<T> | null>;
  remove: (key: string) => Promise<void>;
}
```

### FormPersistenceData

```typescript
interface FormPersistenceData<T> {
  groups: FormGroup<T>[];
  timestamp: number;
  version: string;
  metadata?: Record<string, any>;
}
```