-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathForm.tsx
More file actions
134 lines (115 loc) · 3.64 KB
/
Form.tsx
File metadata and controls
134 lines (115 loc) · 3.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import { ReactNode, useMemo } from "react";
import { DeepPartial, FieldValues, Resolver, SubmitHandler, useForm, UseFormReturn } from "react-hook-form";
import { jsonIsoDateReviver } from "./helpers/dateUtils";
import { FormContext, FormContextProps } from "./context/FormContext";
import { AutoSubmitConfig, useAutoSubmit } from "./hooks/useAutoSubmit";
import { RequiredFieldPath } from "./types/Form";
export interface FormMethods<T extends FieldValues> extends UseFormReturn<T, unknown>, FormContextProps<T> {}
interface FormProps<T extends FieldValues> {
/**
* will be executed when an submit action was triggered
*/
onSubmit: SubmitHandler<T>;
/**
* the resolver for the validation
*/
resolver?: Resolver<T>;
/**
* the default values of the form
*/
defaultValues?: DeepPartial<T>;
/**
* passed field names will be marked with "*"
*/
requiredFields?: RequiredFieldPath<T>[];
/**
* disable all fields inside the form making it readonly
*/
disabled?: boolean;
/**
* enables the form to do an autosubmit on values changed
*/
autoSubmitConfig?: AutoSubmitConfig;
/**
* the children that will be drawn inside the form
*/
children: ((formMethods: FormMethods<T>) => ReactNode) | ReactNode;
/**
* the form ref
*/
formRef?: React.MutableRefObject<HTMLFormElement | null>;
/**
* hide the validation messages for all form inputs.
*/
hideValidationMessages?: boolean;
/**
* controls browser autocomplete behavior for the form.
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete
*/
autoComplete?: string;
}
const Form = <T extends FieldValues>({
children,
onSubmit,
resolver,
defaultValues,
requiredFields = [],
disabled = false,
autoSubmitConfig,
formRef,
hideValidationMessages = false,
autoComplete,
}: FormProps<T>) => {
const revivedDefaultValues = defaultValues
? (JSON.parse(JSON.stringify(defaultValues), jsonIsoDateReviver) as DeepPartial<T>)
: defaultValues;
const disableAriaAutocomplete = autoComplete === "off";
const formMethods = useForm<T>({ resolver, defaultValues: revivedDefaultValues });
const autoSubmitHandler = useAutoSubmit({ onSubmit, formMethods, autoSubmitConfig });
// Memoize the object passed to function children to avoid creating new reference each render
const formPropsForChildren = useMemo(
() => ({
...formMethods,
disabled,
requiredFields,
hideValidationMessages,
disableAriaAutocomplete,
}),
[formMethods, disabled, requiredFields, hideValidationMessages, disableAriaAutocomplete],
);
// Memoize context value to prevent unnecessary re-renders of context consumers
const contextValue = useMemo(
() => ({
requiredFields,
disabled,
hideValidationMessages,
disableAriaAutocomplete,
...formMethods,
}),
[formMethods, requiredFields, disabled, hideValidationMessages, disableAriaAutocomplete],
);
// Only recompute children if the props object changes
const resolvedChildren = useMemo(
() => (typeof children === "function" ? children(formPropsForChildren) : children),
[children, formPropsForChildren],
);
console.log("Rendering Form", { contextValue });
console.log("Rendering Form", { formPropsForChildren });
return (
<FormContext.Provider value={contextValue}>
<form
ref={(elem) => {
if (formRef) {
formRef.current = elem;
}
}}
onSubmit={autoSubmitHandler}
method="POST"
autoComplete={autoComplete}
>
{resolvedChildren}
</form>
</FormContext.Provider>
);
};
export { Form, FormProps };