Installation
To install the package, run the following command:
npm install use-persistent-form
Or
Copy & paste this into your app:
import { useCallback, useEffect } from "react";
import {
useForm,
useWatch,
type UseFormProps,
type FieldValues,
type UseFormReturn,
type DefaultValues,
} from "react-hook-form";
type StorageKey = string | Array<string>;
type AsyncDefaultValues<TFieldValues> = (
payload?: unknown
) => Promise<TFieldValues>;
type FormDefaultValues<TFieldValues extends FieldValues = FieldValues> =
| DefaultValues<TFieldValues>
| AsyncDefaultValues<TFieldValues>;
type UsePersistentFormProps<
TFieldValues extends FieldValues = FieldValues,
TContext = any,
> = UseFormProps<TFieldValues, TContext> & {
storageKey: StorageKey;
skipStorageValidation?: boolean;
storage?: Storage;
};
type UsePersistentFormReturn<
TFieldValues extends FieldValues = FieldValues,
TContext = any,
TransformedValues extends FieldValues | undefined = undefined,
> = UseFormReturn<TFieldValues, TContext, TransformedValues> & {
clearData: () => void;
};
function storageKeyToString(storageKey: StorageKey): string {
if (Array.isArray(storageKey)) return storageKey.join();
return storageKey;
}
function getFormDefaultValues<TFieldValues extends FieldValues = FieldValues>(
key: string,
storage: Storage,
initValues?: FormDefaultValues<TFieldValues>
): FormDefaultValues<TFieldValues> | undefined {
const values = storage.getItem(key);
if (!values) {
return undefined;
}
const parsed = JSON.parse(values);
// I think how can I parse the values from storage by schema
// currently, if schema is provided, it's provided via resolver
// but through resolver, there is no way to get it.
// passing schema as another prop feels kind of redundant?
if (typeof parsed === "object" && !Array.isArray(parsed) && parsed != null) {
return {
...initValues,
...parsed,
};
}
return undefined;
}
export function usePersistentForm<
TFieldValues extends FieldValues = FieldValues,
TContext = any,
TTransformedValues extends FieldValues | undefined = undefined,
>({
storage = typeof window != "undefined" ? sessionStorage : undefined,
storageKey,
skipStorageValidation = false,
defaultValues: initValues,
...props
}: UsePersistentFormProps<TFieldValues, TContext>): UsePersistentFormReturn<
TFieldValues,
TContext,
TTransformedValues
> {
if (!storage)
throw new Error(
"usePersistentForm was called on the server. Server doesn't have any access to the storage object."
);
const key = storageKeyToString(storageKey);
const defaultValues = getFormDefaultValues<TFieldValues>(
key,
storage,
initValues
);
const form = useForm<TFieldValues, TContext, TTransformedValues>({
...props,
defaultValues,
});
const watchedValues = useWatch({
control: form.control,
});
useEffect(() => {
storage.setItem(key, JSON.stringify(watchedValues));
}, [watchedValues]);
const clearData = useCallback(() => {
storage.removeItem(key);
}, [key, storage]);
return { ...form, clearData };
}