aboutsummaryrefslogtreecommitdiffstats
path: root/src/utils/hooks/use-form/use-form-values
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils/hooks/use-form/use-form-values')
-rw-r--r--src/utils/hooks/use-form/use-form-values/index.ts1
-rw-r--r--src/utils/hooks/use-form/use-form-values/use-form-values.test.ts69
-rw-r--r--src/utils/hooks/use-form/use-form-values/use-form-values.ts69
3 files changed, 139 insertions, 0 deletions
diff --git a/src/utils/hooks/use-form/use-form-values/index.ts b/src/utils/hooks/use-form/use-form-values/index.ts
new file mode 100644
index 0000000..664a862
--- /dev/null
+++ b/src/utils/hooks/use-form/use-form-values/index.ts
@@ -0,0 +1 @@
+export * from './use-form-values';
diff --git a/src/utils/hooks/use-form/use-form-values/use-form-values.test.ts b/src/utils/hooks/use-form/use-form-values/use-form-values.test.ts
new file mode 100644
index 0000000..f86d910
--- /dev/null
+++ b/src/utils/hooks/use-form/use-form-values/use-form-values.test.ts
@@ -0,0 +1,69 @@
+import { describe, expect, it } from '@jest/globals';
+import { act, renderHook } from '@testing-library/react';
+import type { ChangeEvent } from 'react';
+import { useFormValues } from './use-form-values';
+
+/**
+ * Generate a new change event.
+ *
+ * @param {string} name - The field name.
+ * @param {unknown} value - The new value of the field.
+ * @returns {ChangeEvent<HTMLInputElement>} The event.
+ */
+const generateChangeEvent = (name: string, value: unknown, type = 'text') => {
+ const ev = new Event('change');
+ Object.defineProperty(ev, 'target', {
+ value: {
+ checked: type === 'checkbox' || type === 'radio' ? value : undefined,
+ name,
+ type,
+ value: type === 'checkbox' || type === 'radio' ? undefined : value,
+ },
+ writable: false,
+ });
+
+ return ev as unknown as ChangeEvent<HTMLInputElement>;
+};
+
+describe('useFormValues', () => {
+ const initialValues = {
+ foo: 'hello',
+ bar: false,
+ };
+ const newValues = {
+ foo: 'world',
+ bar: true,
+ };
+
+ it('can initialize the values', () => {
+ const { result } = renderHook(() => useFormValues(initialValues));
+
+ expect(result.current.values.bar).toBe(initialValues.bar);
+ expect(result.current.values.foo).toBe(initialValues.foo);
+ });
+
+ it('can update and reset the values', () => {
+ const { result } = renderHook(() => useFormValues(initialValues));
+
+ act(() => {
+ result.current.update(
+ generateChangeEvent('bar', newValues.bar, 'checkbox')
+ );
+ });
+
+ expect(result.current.values.bar).toBe(newValues.bar);
+
+ act(() => {
+ result.current.update(generateChangeEvent('foo', newValues.foo));
+ });
+
+ expect(result.current.values.foo).toBe(newValues.foo);
+
+ act(() => {
+ result.current.reset();
+ });
+
+ expect(result.current.values.bar).toBe(initialValues.bar);
+ expect(result.current.values.foo).toBe(initialValues.foo);
+ });
+});
diff --git a/src/utils/hooks/use-form/use-form-values/use-form-values.ts b/src/utils/hooks/use-form/use-form-values/use-form-values.ts
new file mode 100644
index 0000000..8a0962f
--- /dev/null
+++ b/src/utils/hooks/use-form/use-form-values/use-form-values.ts
@@ -0,0 +1,69 @@
+import {
+ type ChangeEventHandler,
+ useCallback,
+ useState,
+ type ChangeEvent,
+} from 'react';
+
+const isBooleanField = (
+ target: EventTarget & (HTMLInputElement | HTMLTextAreaElement)
+): target is EventTarget & HTMLInputElement =>
+ target.type === 'checkbox' || target.type === 'radio';
+
+export type UseFormValuesReturn<T extends Record<string, unknown>> = {
+ /**
+ * A method to reset the fields to their initial values.
+ *
+ * @returns {void}
+ */
+ reset: () => void;
+ /**
+ * A method to handle input or textarea update.
+ *
+ * @param {ChangeEvent<HTMLTextAreaElement | HTMLInputElement>} e - The event.
+ * @returns {void}
+ */
+ update: (e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => void;
+ /**
+ * The fields values.
+ */
+ values: T;
+};
+
+/**
+ * React hook to handle form values update and reset.
+ *
+ * @template {object} T - The object keys should match the fields name.
+ * @param {T} initialValues - The fields initial values.
+ * @returns {UseFormValuesReturn<T>} An object with values and two methods.
+ */
+export const useFormValues = <T extends Record<string, unknown>>(
+ initialValues: T
+): UseFormValuesReturn<T> => {
+ const [values, setValues] = useState(initialValues);
+
+ /**
+ * Reset the field to their initial values.
+ */
+ const reset = useCallback(() => {
+ setValues(initialValues);
+ }, [initialValues]);
+
+ /**
+ * Handle input and textarea update.
+ *
+ * @param {ChangeEvent<HTMLTextAreaElement | HTMLInputElement>} e - The event.
+ * @returns {void}
+ */
+ const update: ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement> =
+ useCallback(({ target }) => {
+ setValues((prevData) => {
+ return {
+ ...prevData,
+ [target.name]: isBooleanField(target) ? target.checked : target.value,
+ };
+ });
+ }, []);
+
+ return { values, reset, update };
+};