@form-guardian/react
React хуки и компоненты для автосохранения форм. Построено поверх @form-guardian/dom.
Установка
npm install @form-guardian/react
Peer зависимости:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
useFormAutosave(formId, options)
Основной React хук для автосохранения форм.
useFormAutosave в настоящее время нестабилен и может иметь проблемы в определенных случаях использования. Для production приложений рекомендуется использовать attachFormAutosave из @form-guardian/dom напрямую, что более стабильно и надежно.
Импорт:
import { useFormAutosave } from '@form-guardian/react';
Параметры:
formId(string, обязательный) - Уникальный идентификатор формыoptions(FormAutosaveOptions, опциональный) - Опции конфигурации
Возвращает: UseFormAutosaveReturn
Пример:
import { useFormAutosave } from '@form-guardian/react';
function MyForm() {
const {
formRef,
hasDraft,
draftTimestamp,
restoreDraft,
clearDraft,
getDraftValues,
restoreValues,
saveValues,
} = useFormAutosave('my-form', {
autoRestore: true,
debounceMs: 300,
batchSaveInterval: 5000, // Сохранение пакетами каждые 5 секунд
onBeforeSave: async (values) => {
console.log('Собираемся сохранить:', values);
},
onAfterSave: async (values) => {
console.log('Успешно сохранено:', values);
},
});
return (
<form ref={formRef}>
{hasDraft && (
<div className="draft-banner">
Черновик сохранен: {new Date(draftTimestamp).toLocaleString()}
</div>
)}
{/* поля формы */}
</form>
);
}
UseFormAutosaveReturn
Объект, возвращаемый хуком useFormAutosave().
Свойства
formRef: RefObject<HTMLFormElement>
Ref для привязки к элементу формы. Обязательно.
<form ref={formRef}>...</form>
hasDraft: boolean
Существует ли черновик для этой формы.
{hasDraft && <div>У вас есть несохраненные изменения</div>}
draftTimestamp: number | null
Временная метка последнего обновления черновика, или null если черновика нет.
{draftTimestamp && (
<div>Последнее сохранение: {new Date(draftTimestamp).toLocaleString()}</div>
)}
Методы
restoreDraft(): Promise<void>
Вручную восстановить черновик в форму. Обычно не нужно при autoRestore: true.
<button onClick={() => restoreDraft()}>Восстановить черновик</button>
clearDraft(): Promise<void>
Очистить черновик из хранилища.
const handleSubmit = async (e) => {
e.preventDefault();
await clearDraft();
// Отправить форму...
};
getDraftValues(): Promise<T | null>
Получить значения черновика как объект без восстановления в форму.
const values = await getDraftValues();
console.log(values); // { name: 'Иван', email: 'ivan@example.com' }
restoreValues(setValue, getValues): Promise<void>
Восстановить значения для контролируемых компонентов (React Hook Form, Formik и т.д.).
// Пример React Hook Form
const { setValue, getValues } = useForm();
const handleRestore = async () => {
await restoreValues(setValue, getValues);
};
saveValues(): Promise<void>
Вручную сохранить текущие значения формы. Полезно для контролируемых форм.
<button onClick={() => saveValues()}>Сохранить черновик</button>
useDraftStatus(formId, options)
Легковесный хук для получения статуса черновика без взаимодействия с DOM. Идеален для отображения индикаторов черновик а вне компонента формы.
Импорт:
import { useDraftStatus } from '@form-guardian/react';
Параметры:
formId(string, обязательный) - Уникальный идентификатор формыoptions(UseDraftStatusOptions, опциональный) - Опции конфигурации
Возвращает: UseDraftStatusResult
Пример:
import { useDraftStatus } from '@form-guardian/react';
function DraftIndicator() {
const {
hasDraft,
updatedAt,
isExpired,
isChecking,
refresh,
clear
} = useDraftStatus('my-form', {
ttl: { days: 7 },
});
if (isChecking) return <div>Проверка...</div>;
if (!hasDraft) return null;
if (isExpired) return <div>Черновик истек</div>;
return (
<div>
Черновик сохранен: {new Date(updatedAt).toLocaleString()}
<button onClick={clear}>Очистить</button>
<button onClick={refresh}>Обновить</button>
</div>
);
}
UseDraftStatusResult
Объект, возвращаемый хуком useDraftStatus().
Свойства
hasDraft: boolean
Существует ли черновик.
isExpired: boolean
Истек ли черновик (на основе TTL).
updatedAt: number | null
Временная метка последнего обновления черновика.
isChecking: boolean
Проверяется ли статус в данный момент.
Методы
refresh(): Promise<void>
Вручную обн овить статус черновика из хранилища.
clear(): Promise<void>
Очистить черновик из хранилища.
Примеры использования
Неконтролируемая форма
import { useFormAutosave } from '@form-guardian/react';
function ContactForm() {
const { formRef, hasDraft, clearDraft } = useFormAutosave('contact-form', {
autoRestore: true,
debounceMs: 500,
});
const handleSubmit = async (e) => {
e.preventDefault();
await clearDraft();
// Отправить форму...
};
return (
<form ref={formRef} onSubmit={handleSubmit}>
{hasDraft && <div className="alert">Обнаружены несохраненные изменения</div>}
<input name="name" placeholder="Имя" />
<input name="email" type="email" placeholder="Email" />
<button type="submit">Отправить</button>
</form>
);
}
React Hook Form (Контролируемая)
import { useForm } from 'react-hook-form';
import { useFormAutosave } from '@form-guardian/react';
import { useEffect } from 'react';
function MyForm() {
const { register, handleSubmit, setValue, getValues, watch } = useForm();
const { formRef, restoreValues, clearDraft, saveValues } = useFormAutosave(
'my-form',
{
autoRestore: false, // Ручное восстановление для контролируемых форм
debounceMs: 300,
}
);
// Восстановить черновик при монтировании
useEffect(() => {
restoreValues(setValue, getValues);
}, []);
// Автосохранение при изменении полей
useEffect(() => {
const subscription = watch(() => {
saveValues();
});
return () => subscription.unsubscribe();
}, [watch]);
const onSubmit = async (data) => {
await clearDraft();
// Отправить данные...
};
return (
<form ref={formRef} onSubmit={handleSubmit(onSubmit)}>
<input {...register('name')} />
<input {...register('email')} />
<button type="submit">Отправить</button>
</form>
);
}
Индикатор статуса черновика (отдельный компонент)
import { useDraftStatus } from '@form-guardian/react';
function Header() {
const { hasDraft, updatedAt } = useDraftStatus('contact-form');
return (
<header>
<h1>Мое приложение</h1>
{hasDraft && (
<div className="draft-badge">
📝 Несохраненный черновик от {new Date(updatedAt).toLocaleTimeString()}
</div>
)}
</header>
);
}