import * as React from 'react'; import { readLocalStorage } from './useLocalStorageState'; export interface IFormDraft { value: T; setValue: React.Dispatch>; /** Форма отличается от initial — есть что сбрасывать. */ isDirty: boolean; /** Вернуть initial и забыть черновик (кнопка «Очистить», успешное сохранение). */ reset: () => void; } function removeDraft(key: string): void { try { window.localStorage.removeItem(key); } catch { // приватный режим — черновика и не было } } /** * Состояние формы создания, которое переживает закрытие модалки (случайный клик * по подложке, «Отмена», F5): черновик лежит в localStorage под key, пока его * не сбросят явно — reset() после успешного сохранения или по кнопке «Очистить». * * key === undefined — обычный useState без персиста: так форма редактирования * существующей записи всегда открывается с актуальными данными с сервера, а не * с устаревшей правкой. */ export function useFormDraft(key: string | undefined, initial: T): IFormDraft { const [value, setValue] = React.useState(() => (key ? readLocalStorage(key, initial) : initial)); // initial у вызывающих — литерал, пересоздаётся каждый рендер; сравниваем по содержимому. const initialJson = JSON.stringify(initial); const isDirty = JSON.stringify(value) !== initialJson; React.useEffect(() => { if (!key) return; if (!isDirty) { removeDraft(key); return; } try { window.localStorage.setItem(key, JSON.stringify(value)); } catch { // приватный режим / переполненная квота — не критично, просто не персистим } }, [key, value, isDirty]); // Ключ удаляем синхронно, а не через эффект: после успешного сохранения модалка // тут же размонтируется, и эффект на новое значение уже не отработает. const reset = (): void => { if (key) removeDraft(key); setValue(JSON.parse(initialJson) as T); }; return { value, setValue, isDirty, reset }; }