npm package: @form-guardian/react
Description: React hooks for form autosave functionality. Provides useFormAutosave hook for easy integration with React forms and useDraftStatus hook for real-time draft status monitoring. Works seamlessly with React Hook Form, Formik, and plain React forms.
Author: Tishkin Yuri
Latest version: 1.0.5

# @form-guardian/react

React hooks for form autosave functionality. Provides `useFormAutosave` hook for easy integration with React forms and `useDraftStatus` hook for real-time draft status monitoring. Works seamlessly with React Hook Form, Formik, and plain React forms.

> ⚠️ **Warning:** `useFormAutosave` is currently **unstable** and may have issues with certain use cases. For production applications, consider using `attachFormAutosave` from `@form-guardian/dom` directly, which is more stable and reliable.

## Overview

`@form-guardian/react` provides React-specific hooks that wrap the universal `@form-guardian/dom` functionality. It offers a more React-idiomatic API with hooks, automatic cleanup, and type-safe integration with React form libraries.

## Installation

```bash
npm install @form-guardian/react
```

**Peer Dependencies:**
- React >= 16.8.0 (hooks support required)

## Features

- ✅ **React Hooks API** - `useFormAutosave` and `useDraftStatus` hooks
- ✅ **TypeScript Support** - Full type inference for form data
- ✅ **Automatic Cleanup** - Handles cleanup on unmount
- ✅ **React Hook Form Integration** - Works seamlessly with RHF
- ✅ **Formik Integration** - Compatible with Formik forms
- ✅ **Plain React Forms** - Works with controlled components
- ✅ **Draft Status Monitoring** - Real-time draft status with `useDraftStatus`

## Quick Start

### Basic Usage

```tsx
import { useFormAutosave } from '@form-guardian/react';

function ContactForm() {
  const { formRef, hasDraft, restoreDraft, clearDraft } = useFormAutosave('contact-form', {
    autoRestore: true,
    onAfterSave: (values) => {
      console.log('Draft saved:', values);
    }
  });

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    // ... submit logic
    await clearDraft(); // Clear draft on successful submit
  };

  return (
    <form ref={formRef} onSubmit={handleSubmit}>
      <input name="name" />
      <input name="email" />
      <button type="submit">Submit</button>
      {hasDraft && (
        <button type="button" onClick={restoreDraft}>
          Restore Draft
        </button>
      )}
    </form>
  );
}
```

### With React Hook Form

```tsx
import { useForm } from 'react-hook-form';
import { useFormAutosave } from '@form-guardian/react';

interface FormData {
  name: string;
  email: string;
  message: string;
}

function MyForm() {
  const { register, handleSubmit, setValue } = useForm<FormData>();
  
  const { formRef, restoreValues } = useFormAutosave<FormData>('my-form', {
    autoRestore: false, // We'll restore manually
  });

  // Restore draft values into React Hook Form
  useEffect(() => {
    restoreValues(setValue);
  }, [restoreValues, setValue]);

  return (
    <form ref={formRef} onSubmit={handleSubmit(onSubmit)}>
      <input {...register('name')} />
      <input {...register('email')} />
      <textarea {...register('message')} />
      <button type="submit">Submit</button>
    </form>
  );
}
```

### With Draft Status

```tsx
import { useFormAutosave, useDraftStatus } from '@form-guardian/react';

function FormWithStatus() {
  const { formRef } = useFormAutosave('my-form');
  
  const { hasDraft, updatedAt, isExpired, isChecking } = useDraftStatus('my-form');

  return (
    <div>
      <form ref={formRef}>
        {/* form fields */}
      </form>
      
      {hasDraft && updatedAt && (
        <div>
          <p>Draft saved: {new Date(updatedAt).toLocaleString()}</p>
          {isExpired && <p>⚠️ Draft has expired</p>}
          {isChecking && <p>Checking draft...</p>}
        </div>
      )}
    </div>
  );
}
```

## API

### `useFormAutosave<T>(formId, options?)`

React hook for form autosave functionality.

> ⚠️ **Warning:** This hook is currently **unstable**. For production use, consider using `attachFormAutosave` from `@form-guardian/dom` directly.

#### Parameters

- **`formId`** - Unique identifier for the form
- **`options`** - Configuration options (same as `attachFormAutosave`)

#### Returns

- **`formRef`** - Ref to attach to form element
- **`hasDraft`** - Boolean indicating if draft exists
- **`draftTimestamp`** - Timestamp of draft (or `null`)
- **`restoreDraft()`** - Function to manually restore draft
- **`getDraftValues()`** - Get draft values as object
- **`restoreValues(setValue, getDefaultValues?)`** - Restore values for controlled components
- **`clearDraft()`** - Clear the draft
- **`saveValues(values?)`** - Manually save values

### `useDraftStatus<T>(formId, options?)`

Lightweight hook for checking draft status without touching the DOM.

#### Parameters

- **`formId`** - Unique identifier for the form
- **`options`** - Configuration options:
  - **`includeOrigin`** - Include origin in draft ID (default: `true`)
  - **`storagePrefix`** - Storage prefix (default: `'fg'`)
  - **`ttl`** - Time to live for drafts

#### Returns

- **`hasDraft`** - Boolean indicating if draft exists
- **`isExpired`** - Boolean indicating if draft has expired
- **`updatedAt`** - Timestamp of last update (or `null`)
- **`isChecking`** - Boolean indicating if status is being checked
- **`refresh()`** - Function to manually refresh status
- **`clear()`** - Function to clear the draft

## Examples

### With Analytics Events

```tsx
const { formRef } = useFormAutosave<FormData>('checkout-form', {
  onBeforeSave: async (values) => {
    analytics.track('draft_save_started');
  },
  onAfterSave: async (values) => {
    analytics.track('draft_saved', { fieldCount: Object.keys(values).length });
  },
  onBeforeRestore: async (values) => {
    analytics.track('draft_restore_started');
  },
  onAfterRestore: async (values) => {
    analytics.track('draft_restored');
  },
  onDraftExpired: async (draftId) => {
    analytics.track('draft_expired', { draftId });
  }
});
```

### With Batching

```tsx
const { formRef } = useFormAutosave('large-form', {
  batchSaveInterval: 5000, // Save every 5 seconds
  onAfterSave: (values) => {
    console.log('Batch saved');
  }
});
```

### TypeScript with Custom Types

```tsx
interface ContactFormData {
  name: string;
  email: string;
  phone: string;
  message: string;
}

function ContactForm() {
  const { formRef, getDraftValues } = useFormAutosave<ContactFormData>('contact-form', {
    onAfterSave: (values) => {
      // values is typed as ContactFormData
      console.log(values.name); // TypeScript knows this exists
    }
  });

  const handleLoadDraft = async () => {
    const draft = await getDraftValues();
    if (draft) {
      // draft is typed as ContactFormData | null
      console.log(draft.email);
    }
  };

  return <form ref={formRef}>...</form>;
}
```

### Draft Status Banner

```tsx
function DraftBanner({ formId }: { formId: string }) {
  const { hasDraft, updatedAt, isExpired, clear } = useDraftStatus(formId, {
    ttl: { days: 7 }
  });

  if (!hasDraft) return null;

  return (
    <div className="draft-banner">
      {isExpired ? (
        <p>⚠️ Your draft has expired.</p>
      ) : (
        <p>📝 You have a draft from {new Date(updatedAt!).toLocaleString()}</p>
      )}
      <button onClick={() => clear()}>Discard</button>
    </div>
  );
}
```

## When to Use

Use `@form-guardian/react` if you:
- Are building a React application
- Want a React-idiomatic API with hooks
- Need type-safe integration with React form libraries
- Want automatic cleanup and lifecycle management
- Need real-time draft status monitoring

For non-React applications or custom integrations, use `@form-guardian/dom` instead.