mobx-react-hook-form 2.1.3 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -39,11 +39,8 @@ class YourVM {
39
39
 
40
40
 
41
41
  const YourView = () => {
42
- const form = useMobxForm(yourVM.form)
43
-
44
-
45
42
  return (
46
- <form onSubmit={form.onSubmit} onReset={form.onReset}>
43
+ <form onSubmit={yourVM.form.submit} onReset={yourVM.form.reset}>
47
44
  <Controller control={form.control} name={'username'} render={...} />
48
45
  </form>
49
46
  )
@@ -1,33 +1,249 @@
1
- import { createFormControl, FieldValues, SubmitErrorHandler, SubmitHandler, UseFormProps } from 'react-hook-form';
2
- import { MobxFormStateReadonly } from './mobx-form-state.js';
1
+ import { BaseSyntheticEvent } from 'react';
2
+ import { Control, createFormControl, DeepMap, DeepPartial, FieldErrors, FieldValues, FormState, UseFormClearErrors, UseFormRegister, UseFormReset, UseFormResetField, UseFormSetError, UseFormSetFocus, UseFormTrigger, UseFormUnregister } from 'react-hook-form';
3
3
  import { MobxFormParams } from './mobx-form.types.js';
4
- export declare class MobxForm<TFieldValues extends FieldValues = FieldValues, TContext = any, TTransformedValues = TFieldValues> {
4
+ type FormFullState<TFieldValues extends FieldValues> = FormState<TFieldValues> & {
5
+ values: TFieldValues;
6
+ };
7
+ export declare class MobxForm<TFieldValues extends FieldValues = FieldValues, TContext = any, TTransformedValues = TFieldValues> implements FormFullState<TFieldValues> {
5
8
  private config;
6
- protected abortController: AbortController;
9
+ values: TFieldValues;
10
+ isDirty: boolean;
11
+ isLoading: boolean;
12
+ isSubmitted: boolean;
13
+ isSubmitSuccessful: boolean;
14
+ isSubmitting: boolean;
15
+ isValidating: boolean;
16
+ isValid: boolean;
17
+ disabled: boolean;
18
+ submitCount: number;
19
+ defaultValues: Readonly<DeepPartial<TFieldValues>> | undefined;
20
+ dirtyFields: Partial<Readonly<DeepMap<DeepPartial<TFieldValues>, boolean>>>;
21
+ touchedFields: Partial<Readonly<DeepMap<DeepPartial<TFieldValues>, boolean>>>;
22
+ validatingFields: Partial<Readonly<DeepMap<DeepPartial<TFieldValues>, boolean>>>;
23
+ errors: FieldErrors<TFieldValues>;
24
+ isReady: boolean;
7
25
  /**
8
- * Real react-hook-form params
9
- * Needed to connect real react-hook-form to this mobx wrapper
26
+ * Set an error for the field. When set an error which is not associated to a field then manual `clearErrors` invoke is required.
27
+ *
28
+ * @remarks
29
+ * [API](https://react-hook-form.com/docs/useform/seterror) • [Demo](https://codesandbox.io/s/react-hook-form-v7-ts-seterror-nfxxu) • [Video](https://www.youtube.com/watch?v=raMqvE0YyIY)
30
+ *
31
+ * @param name - the path name to the form field value.
32
+ * @param error - an error object which contains type and optional message
33
+ * @param options - whether or not to focus on the field
34
+ *
35
+ * @example
36
+ * ```tsx
37
+ * // when the error is not associated with any fields, `clearError` will need to invoke to clear the error
38
+ * const onSubmit = () => setError("serverError", { type: "server", message: "Error occurred"})
39
+ *
40
+ * <button onClick={() => setError("name", { type: "min" })} />
41
+ *
42
+ * // focus on the input after setting the error
43
+ * <button onClick={() => setError("name", { type: "max" }, { shouldFocus: true })} />
44
+ * ```
10
45
  */
11
- params: UseFormProps<TFieldValues, TContext, TTransformedValues>;
12
- protected handleSubmit(...args: Parameters<SubmitHandler<TTransformedValues>>): void | Promise<void>;
13
- protected handleSubmitFailed(...args: Parameters<SubmitErrorHandler<TFieldValues>>): void | Promise<void>;
14
- protected handleReset(): void;
46
+ setError: UseFormSetError<TFieldValues>;
15
47
  /**
16
- * Original react-hook-form form
48
+ * Clear the entire form errors.
49
+ *
50
+ * @remarks
51
+ * [API](https://react-hook-form.com/docs/useform/clearerrors) • [Demo](https://codesandbox.io/s/react-hook-form-v7-ts-clearerrors-w3ymx)
52
+ *
53
+ * @param name - the path name to the form field value.
54
+ *
55
+ * @example
56
+ * Clear all errors
57
+ * ```tsx
58
+ * clearErrors(); // clear the entire form error
59
+ * clearErrors(["name", "name1"]) // clear an array of fields' error
60
+ * clearErrors("name2"); // clear a single field error
61
+ * ```
62
+ */
63
+ clearErrors: UseFormClearErrors<TFieldValues>;
64
+ /**
65
+ * Trigger field or form validation
66
+ *
67
+ * @remarks
68
+ * [API](https://react-hook-form.com/docs/useform/trigger) • [Demo](https://codesandbox.io/s/react-hook-form-v7-ts-triggervalidation-forked-xs7hl) • [Video](https://www.youtube.com/watch?v=-bcyJCDjksE)
69
+ *
70
+ * @param name - provide empty argument will trigger the entire form validation, an array of field names will validate an array of fields, and a single field name will only trigger that field's validation.
71
+ * @param options - should focus on the error field
72
+ *
73
+ * @returns validation result
74
+ *
75
+ * @example
76
+ * ```tsx
77
+ * useEffect(() => {
78
+ * trigger();
79
+ * }, [trigger])
80
+ *
81
+ * <button onClick={async () => {
82
+ * const result = await trigger(); // result will be a boolean value
83
+ * }}>
84
+ * trigger
85
+ * </button>
86
+ * ```
87
+ */
88
+ trigger: UseFormTrigger<TFieldValues>;
89
+ /**
90
+ * Reset a field state and reference.
91
+ *
92
+ * @remarks
93
+ * [API](https://react-hook-form.com/docs/useform/resetfield) • [Demo](https://codesandbox.io/s/priceless-firefly-d0kuv) • [Video](https://www.youtube.com/watch?v=IdLFcNaEFEo)
94
+ *
95
+ * @param name - the path name to the form field value.
96
+ * @param options - keep form state options
97
+ *
98
+ * @example
99
+ * ```tsx
100
+ * <input {...register("firstName", { required: true })} />
101
+ * <button type="button" onClick={() => resetField("firstName"))}>Reset</button>
102
+ * ```
103
+ */
104
+ resetField: UseFormResetField<TFieldValues>;
105
+ /**
106
+ * Unregister a field reference and remove its value.
107
+ *
108
+ * @remarks
109
+ * [API](https://react-hook-form.com/docs/useform/unregister) • [Demo](https://codesandbox.io/s/react-hook-form-unregister-4k2ey) • [Video](https://www.youtube.com/watch?v=TM99g_NW5Gk&feature=emb_imp_woyt)
110
+ *
111
+ * @param name - the path name to the form field value.
112
+ * @param options - keep form state options
113
+ *
114
+ * @example
115
+ * ```tsx
116
+ * register("name", { required: true })
117
+ *
118
+ * <button onClick={() => unregister("name")} />
119
+ * // there are various keep options to retain formState
120
+ * <button onClick={() => unregister("name", { keepErrors: true })} />
121
+ * ```
122
+ */
123
+ unregister: UseFormUnregister<TFieldValues>;
124
+ /**
125
+ * Form control
126
+ *
127
+ * @remarks
128
+ * [API](https://react-hook-form.com/docs/useform/control)
129
+ *
17
130
  */
18
- form: ReturnType<typeof createFormControl<TFieldValues, TContext, TTransformedValues>>;
131
+ control: Control<TFieldValues, TContext, TTransformedValues>;
19
132
  /**
20
- * form state received from form.formState
133
+ * Register field into hook form with or without the actual DOM ref. You can invoke register anywhere in the component including at `useEffect`.
134
+ *
135
+ * @remarks
136
+ * [API](https://react-hook-form.com/docs/useform/register) • [Demo](https://codesandbox.io/s/react-hook-form-register-ts-ip2j3) • [Video](https://www.youtube.com/watch?v=JFIpCoajYkA)
137
+ *
138
+ * @param name - the path name to the form field value, name is required and unique
139
+ * @param options - register options include validation, disabled, unregister, value as and dependent validation
140
+ *
141
+ * @returns onChange, onBlur, name, ref, and native contribute attribute if browser validation is enabled.
142
+ *
143
+ * @example
144
+ * ```tsx
145
+ * // Register HTML native input
146
+ * <input {...register("input")} />
147
+ * <select {...register("select")} />
148
+ *
149
+ * // Register options
150
+ * <textarea {...register("textarea", { required: "This is required.", maxLength: 20 })} />
151
+ * <input type="number" {...register("name2", { valueAsNumber: true })} />
152
+ * <input {...register("name3", { deps: ["name2"] })} />
153
+ *
154
+ * // Register custom field at useEffect
155
+ * useEffect(() => {
156
+ * register("name4");
157
+ * register("name5", { value: "hiddenValue" });
158
+ * }, [register])
159
+ *
160
+ * // Register without ref
161
+ * const { onChange, onBlur, name } = register("name6")
162
+ * <input onChange={onChange} onBlur={onBlur} name={name} />
163
+ * ```
21
164
  */
22
- private _state;
23
- get state(): MobxFormStateReadonly<TFieldValues>;
165
+ register: UseFormRegister<TFieldValues>;
24
166
  /**
25
- * Raw data received from form.getValues()
167
+ * Set focus on a registered field. You can start to invoke this method after all fields are mounted to the DOM.
168
+ *
169
+ * @remarks
170
+ * [API](https://react-hook-form.com/docs/useform/setfocus) • [Demo](https://codesandbox.io/s/setfocus-rolus)
171
+ *
172
+ * @param name - the path name to the form field value.
173
+ * @param options - input focus behavior options
174
+ *
175
+ * @example
176
+ * ```tsx
177
+ * useEffect(() => {
178
+ * setFocus("name");
179
+ * }, [setFocus])
180
+ * // shouldSelect allows to select input's content on focus
181
+ * <button onClick={() => setFocus("name", { shouldSelect: true })}>Focus</button>
182
+ * ```
26
183
  */
27
- get data(): TFieldValues;
184
+ setFocus: UseFormSetFocus<TFieldValues>;
185
+ /**
186
+ * Reset at the entire form state.
187
+ *
188
+ * @remarks
189
+ * [API](https://react-hook-form.com/docs/useform/reset) • [Demo](https://codesandbox.io/s/react-hook-form-reset-v7-ts-pu901) • [Video](https://www.youtube.com/watch?v=qmCLBjyPwVk)
190
+ *
191
+ * @param values - the entire form values to be reset
192
+ * @param keepStateOptions - keep form state options
193
+ *
194
+ * @example
195
+ * ```tsx
196
+ * useEffect(() => {
197
+ * // reset the entire form after component mount or form defaultValues is ready
198
+ * reset({
199
+ * fieldA: "test"
200
+ * fieldB: "test"
201
+ * });
202
+ * }, [reset])
203
+ *
204
+ * // reset by combine with existing form values
205
+ * reset({
206
+ * ...getValues(),
207
+ * fieldB: "test"
208
+ *});
209
+ *
210
+ * // reset and keep form state
211
+ * reset({
212
+ * ...getValues(),
213
+ *}, {
214
+ * keepErrors: true,
215
+ * keepDirty: true
216
+ *});
217
+ * ```
218
+ */
219
+ resetForm: UseFormReset<TFieldValues>;
220
+ protected abortController: AbortController;
221
+ protected lastRafId: number | undefined;
222
+ /**
223
+ * Original react-hook-form form
224
+ */
225
+ originalForm: ReturnType<typeof createFormControl<TFieldValues, TContext, TTransformedValues>>;
28
226
  constructor(config: MobxFormParams<TFieldValues, TContext, TTransformedValues>);
227
+ /**
228
+ * Method to manually submit form.
229
+ * Used to attach this method to <form /> element
230
+ *
231
+ * @example
232
+ *
233
+ * <form onSubmit={form.submit} />
234
+ */
235
+ submit(e?: BaseSyntheticEvent): Promise<TTransformedValues>;
236
+ /**
237
+ * Method to manually reset all form.
238
+ * Used to attach this method to <form /> element
239
+ *
240
+ * @example
241
+ *
242
+ * <form onReset={form.reset} />
243
+ */
244
+ reset(e?: BaseSyntheticEvent): void;
245
+ private updateFormState;
29
246
  destroy(): void;
30
- submit: (event?: any) => Promise<void>;
31
- reset: () => void;
32
247
  }
248
+ export {};
33
249
  //# sourceMappingURL=mobx-form.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mobx-form.d.ts","sourceRoot":"","sources":["../../src/mobx-form/mobx-form.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,iBAAiB,EACjB,WAAW,EACX,kBAAkB,EAClB,aAAa,EACb,YAAY,EACb,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAiB,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,qBAAa,QAAQ,CACnB,YAAY,SAAS,WAAW,GAAG,WAAW,EAC9C,QAAQ,GAAG,GAAG,EACd,kBAAkB,GAAG,YAAY;IAuD/B,OAAO,CAAC,MAAM;IArDhB,SAAS,CAAC,eAAe,EAAE,eAAe,CAAC;IAE3C;;;OAGG;IACH,MAAM,EAAE,YAAY,CAAC,YAAY,EAAE,QAAQ,EAAE,kBAAkB,CAAC,CAAC;IAEjE,SAAS,CAAC,YAAY,CAEpB,GAAG,IAAI,EAAE,UAAU,CAAC,aAAa,CAAC,kBAAkB,CAAC,CAAC,GACrD,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvB,SAAS,CAAC,kBAAkB,CAE1B,GAAG,IAAI,EAAE,UAAU,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC,GACpD,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvB,SAAS,CAAC,WAAW;IAKrB;;OAEG;IACH,IAAI,EAAE,UAAU,CACd,OAAO,iBAAiB,CAAC,YAAY,EAAE,QAAQ,EAAE,kBAAkB,CAAC,CACrE,CAAC;IAEF;;OAEG;IACH,OAAO,CAAC,MAAM,CAA8B;IAE5C,IAAI,KAAK,IAAI,qBAAqB,CAAC,YAAY,CAAC,CAE/C;IAED;;OAEG;IACH,IAAI,IAAI,IAAI,YAAY,CAEvB;gBAGS,MAAM,EAAE,cAAc,CAAC,YAAY,EAAE,QAAQ,EAAE,kBAAkB,CAAC;IAkD5E,OAAO,IAAI,IAAI;IAIf,MAAM,GAAU,QAAQ,GAAG,mBAOzB;IAEF,KAAK,aAEH;CACH"}
1
+ {"version":3,"file":"mobx-form.d.ts","sourceRoot":"","sources":["../../src/mobx-form/mobx-form.ts"],"names":[],"mappings":"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"}
@@ -1,60 +1,244 @@
1
1
  /* eslint-disable @typescript-eslint/ban-ts-comment */
2
2
  import { LinkedAbortController } from 'linked-abort-controller';
3
- import { action, computed, makeObservable, observable } from 'mobx';
3
+ import { action, comparer, makeObservable, observable } from 'mobx';
4
4
  import { createFormControl, } from 'react-hook-form';
5
- import { MobxFormState } from './mobx-form-state.js';
6
5
  export class MobxForm {
7
6
  config;
8
- abortController;
7
+ values;
8
+ isDirty = false;
9
+ isLoading = false;
10
+ isSubmitted = false;
11
+ isSubmitSuccessful = false;
12
+ isSubmitting = false;
13
+ isValidating = false;
14
+ isValid = false;
15
+ disabled = false;
16
+ submitCount = 0;
17
+ defaultValues;
18
+ dirtyFields = {};
19
+ touchedFields = {};
20
+ validatingFields = {};
21
+ errors = {};
22
+ isReady = false;
9
23
  /**
10
- * Real react-hook-form params
11
- * Needed to connect real react-hook-form to this mobx wrapper
24
+ * Set an error for the field. When set an error which is not associated to a field then manual `clearErrors` invoke is required.
25
+ *
26
+ * @remarks
27
+ * [API](https://react-hook-form.com/docs/useform/seterror) • [Demo](https://codesandbox.io/s/react-hook-form-v7-ts-seterror-nfxxu) • [Video](https://www.youtube.com/watch?v=raMqvE0YyIY)
28
+ *
29
+ * @param name - the path name to the form field value.
30
+ * @param error - an error object which contains type and optional message
31
+ * @param options - whether or not to focus on the field
32
+ *
33
+ * @example
34
+ * ```tsx
35
+ * // when the error is not associated with any fields, `clearError` will need to invoke to clear the error
36
+ * const onSubmit = () => setError("serverError", { type: "server", message: "Error occurred"})
37
+ *
38
+ * <button onClick={() => setError("name", { type: "min" })} />
39
+ *
40
+ * // focus on the input after setting the error
41
+ * <button onClick={() => setError("name", { type: "max" }, { shouldFocus: true })} />
42
+ * ```
12
43
  */
13
- params;
14
- handleSubmit(
15
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
16
- ...args) {
17
- this.config.onSubmit?.(...args);
18
- // used to override
19
- }
20
- handleSubmitFailed(
21
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
22
- ...args) {
23
- this.config.onSubmitFailed?.(...args);
24
- // used to override
25
- }
26
- handleReset() {
27
- this.config.onReset?.();
28
- // used to override
29
- }
44
+ setError;
30
45
  /**
31
- * Original react-hook-form form
46
+ * Clear the entire form errors.
47
+ *
48
+ * @remarks
49
+ * [API](https://react-hook-form.com/docs/useform/clearerrors) • [Demo](https://codesandbox.io/s/react-hook-form-v7-ts-clearerrors-w3ymx)
50
+ *
51
+ * @param name - the path name to the form field value.
52
+ *
53
+ * @example
54
+ * Clear all errors
55
+ * ```tsx
56
+ * clearErrors(); // clear the entire form error
57
+ * clearErrors(["name", "name1"]) // clear an array of fields' error
58
+ * clearErrors("name2"); // clear a single field error
59
+ * ```
32
60
  */
33
- form;
61
+ clearErrors;
34
62
  /**
35
- * form state received from form.formState
63
+ * Trigger field or form validation
64
+ *
65
+ * @remarks
66
+ * [API](https://react-hook-form.com/docs/useform/trigger) • [Demo](https://codesandbox.io/s/react-hook-form-v7-ts-triggervalidation-forked-xs7hl) • [Video](https://www.youtube.com/watch?v=-bcyJCDjksE)
67
+ *
68
+ * @param name - provide empty argument will trigger the entire form validation, an array of field names will validate an array of fields, and a single field name will only trigger that field's validation.
69
+ * @param options - should focus on the error field
70
+ *
71
+ * @returns validation result
72
+ *
73
+ * @example
74
+ * ```tsx
75
+ * useEffect(() => {
76
+ * trigger();
77
+ * }, [trigger])
78
+ *
79
+ * <button onClick={async () => {
80
+ * const result = await trigger(); // result will be a boolean value
81
+ * }}>
82
+ * trigger
83
+ * </button>
84
+ * ```
36
85
  */
37
- _state;
38
- get state() {
39
- return this._state;
40
- }
86
+ trigger;
41
87
  /**
42
- * Raw data received from form.getValues()
88
+ * Reset a field state and reference.
89
+ *
90
+ * @remarks
91
+ * [API](https://react-hook-form.com/docs/useform/resetfield) • [Demo](https://codesandbox.io/s/priceless-firefly-d0kuv) • [Video](https://www.youtube.com/watch?v=IdLFcNaEFEo)
92
+ *
93
+ * @param name - the path name to the form field value.
94
+ * @param options - keep form state options
95
+ *
96
+ * @example
97
+ * ```tsx
98
+ * <input {...register("firstName", { required: true })} />
99
+ * <button type="button" onClick={() => resetField("firstName"))}>Reset</button>
100
+ * ```
43
101
  */
44
- get data() {
45
- return this._state.values;
46
- }
102
+ resetField;
103
+ /**
104
+ * Unregister a field reference and remove its value.
105
+ *
106
+ * @remarks
107
+ * [API](https://react-hook-form.com/docs/useform/unregister) • [Demo](https://codesandbox.io/s/react-hook-form-unregister-4k2ey) • [Video](https://www.youtube.com/watch?v=TM99g_NW5Gk&feature=emb_imp_woyt)
108
+ *
109
+ * @param name - the path name to the form field value.
110
+ * @param options - keep form state options
111
+ *
112
+ * @example
113
+ * ```tsx
114
+ * register("name", { required: true })
115
+ *
116
+ * <button onClick={() => unregister("name")} />
117
+ * // there are various keep options to retain formState
118
+ * <button onClick={() => unregister("name", { keepErrors: true })} />
119
+ * ```
120
+ */
121
+ unregister;
122
+ /**
123
+ * Form control
124
+ *
125
+ * @remarks
126
+ * [API](https://react-hook-form.com/docs/useform/control)
127
+ *
128
+ */
129
+ control;
130
+ /**
131
+ * Register field into hook form with or without the actual DOM ref. You can invoke register anywhere in the component including at `useEffect`.
132
+ *
133
+ * @remarks
134
+ * [API](https://react-hook-form.com/docs/useform/register) • [Demo](https://codesandbox.io/s/react-hook-form-register-ts-ip2j3) • [Video](https://www.youtube.com/watch?v=JFIpCoajYkA)
135
+ *
136
+ * @param name - the path name to the form field value, name is required and unique
137
+ * @param options - register options include validation, disabled, unregister, value as and dependent validation
138
+ *
139
+ * @returns onChange, onBlur, name, ref, and native contribute attribute if browser validation is enabled.
140
+ *
141
+ * @example
142
+ * ```tsx
143
+ * // Register HTML native input
144
+ * <input {...register("input")} />
145
+ * <select {...register("select")} />
146
+ *
147
+ * // Register options
148
+ * <textarea {...register("textarea", { required: "This is required.", maxLength: 20 })} />
149
+ * <input type="number" {...register("name2", { valueAsNumber: true })} />
150
+ * <input {...register("name3", { deps: ["name2"] })} />
151
+ *
152
+ * // Register custom field at useEffect
153
+ * useEffect(() => {
154
+ * register("name4");
155
+ * register("name5", { value: "hiddenValue" });
156
+ * }, [register])
157
+ *
158
+ * // Register without ref
159
+ * const { onChange, onBlur, name } = register("name6")
160
+ * <input onChange={onChange} onBlur={onBlur} name={name} />
161
+ * ```
162
+ */
163
+ register;
164
+ /**
165
+ * Set focus on a registered field. You can start to invoke this method after all fields are mounted to the DOM.
166
+ *
167
+ * @remarks
168
+ * [API](https://react-hook-form.com/docs/useform/setfocus) • [Demo](https://codesandbox.io/s/setfocus-rolus)
169
+ *
170
+ * @param name - the path name to the form field value.
171
+ * @param options - input focus behavior options
172
+ *
173
+ * @example
174
+ * ```tsx
175
+ * useEffect(() => {
176
+ * setFocus("name");
177
+ * }, [setFocus])
178
+ * // shouldSelect allows to select input's content on focus
179
+ * <button onClick={() => setFocus("name", { shouldSelect: true })}>Focus</button>
180
+ * ```
181
+ */
182
+ setFocus;
183
+ /**
184
+ * Reset at the entire form state.
185
+ *
186
+ * @remarks
187
+ * [API](https://react-hook-form.com/docs/useform/reset) • [Demo](https://codesandbox.io/s/react-hook-form-reset-v7-ts-pu901) • [Video](https://www.youtube.com/watch?v=qmCLBjyPwVk)
188
+ *
189
+ * @param values - the entire form values to be reset
190
+ * @param keepStateOptions - keep form state options
191
+ *
192
+ * @example
193
+ * ```tsx
194
+ * useEffect(() => {
195
+ * // reset the entire form after component mount or form defaultValues is ready
196
+ * reset({
197
+ * fieldA: "test"
198
+ * fieldB: "test"
199
+ * });
200
+ * }, [reset])
201
+ *
202
+ * // reset by combine with existing form values
203
+ * reset({
204
+ * ...getValues(),
205
+ * fieldB: "test"
206
+ *});
207
+ *
208
+ * // reset and keep form state
209
+ * reset({
210
+ * ...getValues(),
211
+ *}, {
212
+ * keepErrors: true,
213
+ * keepDirty: true
214
+ *});
215
+ * ```
216
+ */
217
+ resetForm;
218
+ abortController;
219
+ lastRafId;
220
+ /**
221
+ * Original react-hook-form form
222
+ */
223
+ originalForm;
47
224
  constructor(config) {
48
225
  this.config = config;
49
- this.params = config;
50
226
  this.abortController = new LinkedAbortController(config.abortSignal);
51
- this.form = createFormControl(config);
52
- this._state = new MobxFormState({
53
- values: this.form.getValues(),
227
+ this.originalForm = createFormControl(config);
228
+ this.setError = this.originalForm.setError;
229
+ this.clearErrors = this.originalForm.clearErrors;
230
+ this.trigger = this.originalForm.trigger;
231
+ this.resetField = this.originalForm.resetField;
232
+ this.unregister = this.originalForm.unregister;
233
+ this.control = this.originalForm.control;
234
+ this.register = this.originalForm.register;
235
+ this.setFocus = this.originalForm.setFocus;
236
+ this.resetForm = this.originalForm.reset;
237
+ Object.assign(this, {
238
+ values: this.originalForm.getValues(),
54
239
  defaultValues: config.defaultValues || {},
55
240
  });
56
- this.params = config;
57
- const subscription = this.form.subscribe({
241
+ const subscription = this.originalForm.subscribe({
58
242
  formState: {
59
243
  values: true,
60
244
  errors: true,
@@ -66,34 +250,120 @@ export class MobxForm {
66
250
  validatingFields: true,
67
251
  },
68
252
  callback: (rawFormState) => {
69
- this._state.update(rawFormState);
253
+ if (this.config.lazyUpdates === false) {
254
+ this.updateFormState(rawFormState);
255
+ }
256
+ else {
257
+ if (this.lastRafId !== undefined) {
258
+ cancelAnimationFrame(this.lastRafId);
259
+ this.lastRafId = undefined;
260
+ }
261
+ this.lastRafId = requestAnimationFrame(() => {
262
+ this.updateFormState(rawFormState);
263
+ this.lastRafId = undefined;
264
+ });
265
+ }
70
266
  },
71
267
  });
268
+ observable.deep(this, 'values');
269
+ observable.ref(this, 'isDirty');
270
+ observable.ref(this, 'isLoading');
271
+ observable.ref(this, 'isSubmitted');
272
+ observable.ref(this, 'isSubmitSuccessful');
273
+ observable.ref(this, 'isSubmitting');
274
+ observable.ref(this, 'isValidating');
275
+ observable.ref(this, 'isValid');
276
+ observable.ref(this, 'disabled');
277
+ observable.ref(this, 'submitCount');
278
+ observable.ref(this, 'isReady');
279
+ observable.deep(this, 'defaultValues');
280
+ observable.deep(this, 'dirtyFields');
281
+ observable.deep(this, 'touchedFields');
282
+ observable.deep(this, 'validatingFields');
283
+ observable.deep(this, 'errors');
284
+ action(this, 'updateFormState');
285
+ observable.ref(this, 'originalForm');
286
+ action.bound(this, 'submit');
287
+ action.bound(this, 'reset');
288
+ makeObservable(this);
72
289
  this.abortController.signal.addEventListener('abort', () => {
73
290
  subscription();
74
291
  // @ts-ignore
75
- this.form = null;
292
+ this.originalForm = null;
76
293
  // @ts-ignore
77
294
  this.data = null;
78
295
  });
79
- computed.struct(this, 'data');
80
- computed.struct(this, 'state');
81
- observable.ref(this, 'params');
82
- observable.ref(this, 'form');
83
- action.bound(this, 'setParams');
84
- action.bound(this, 'updateParams');
85
- action.bound(this, 'syncForm');
86
- makeObservable(this);
296
+ }
297
+ /**
298
+ * Method to manually submit form.
299
+ * Used to attach this method to <form /> element
300
+ *
301
+ * @example
302
+ *
303
+ * <form onSubmit={form.submit} />
304
+ */
305
+ submit(e) {
306
+ return new Promise((resolve, reject) => {
307
+ this.originalForm.handleSubmit((data, event) => {
308
+ this.config.onSubmit?.(data, event);
309
+ resolve(data);
310
+ }, (errors, event) => {
311
+ this.config.onSubmitFailed?.(errors, event);
312
+ reject(errors);
313
+ })(e);
314
+ });
315
+ }
316
+ /**
317
+ * Method to manually reset all form.
318
+ * Used to attach this method to <form /> element
319
+ *
320
+ * @example
321
+ *
322
+ * <form onReset={form.reset} />
323
+ */
324
+ reset(e) {
325
+ this.resetForm();
326
+ this.config.onReset?.(e);
327
+ }
328
+ updateFormState({ values, errors, ...simpleProperties }) {
329
+ Object.entries(simpleProperties).forEach(([key, value]) => {
330
+ if (value != null) {
331
+ // @ts-ignore
332
+ this[key] = value;
333
+ }
334
+ });
335
+ console.info('update form state', errors);
336
+ if (errors) {
337
+ const currentErrorsSet = new Set(Object.keys(this.errors));
338
+ const newErrors = Object.keys(errors);
339
+ for (const errorField of newErrors) {
340
+ if (currentErrorsSet.has(errorField)) {
341
+ currentErrorsSet.delete(errorField);
342
+ if (!comparer.structural(this.errors[errorField], errors[errorField])) {
343
+ // @ts-ignore
344
+ Object.assign(this.errors[errorField], errors[errorField]);
345
+ }
346
+ }
347
+ else {
348
+ // @ts-ignore
349
+ this.errors[errorField] = errors[errorField];
350
+ }
351
+ }
352
+ currentErrorsSet.forEach((errorField) => {
353
+ // @ts-ignore
354
+ delete this.errors[errorField];
355
+ });
356
+ }
357
+ else {
358
+ this.errors = {};
359
+ }
360
+ // @ts-ignore
361
+ this.values = values ?? {};
87
362
  }
88
363
  destroy() {
89
364
  this.abortController.abort();
90
- }
91
- submit = async (event) => {
92
- if (this.config.onSubmit) {
93
- await this.form.handleSubmit(this.config.onSubmit, this.config.onSubmitFailed)(event);
365
+ if (this.lastRafId !== undefined) {
366
+ cancelAnimationFrame(this.lastRafId);
94
367
  }
95
- };
96
- reset = () => {
97
- this.form.reset();
98
- };
368
+ }
99
369
  }
@@ -20,9 +20,13 @@ export interface MobxFormParams<TFieldValues extends FieldValues = FieldValues,
20
20
  /**
21
21
  * Form reset handler
22
22
  */
23
- onReset?: VoidFunction;
23
+ onReset?: (event: any) => void;
24
+ /**
25
+ * lazy mobx form state updates using requestAnimationFrame
26
+ * @default - {true}
27
+ */
28
+ lazyUpdates?: boolean;
24
29
  }
25
- export type ExtractFormFieldValues<T extends AnyMobxForm> = Exclude<T['params']['values'], undefined | null>;
26
- export type ExtractFormContext<T extends AnyMobxForm> = Exclude<T['params']['context'], undefined | null>;
30
+ export type ExtractFormFieldValues<T extends AnyMobxForm> = Exclude<T['values'], undefined | null>;
27
31
  export type ExtractFormFieldOutputValues<T extends AnyMobxForm> = T extends MobxForm<any, any, infer TFieldOutputValues> ? TFieldOutputValues : never;
28
32
  //# sourceMappingURL=mobx-form.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mobx-form.types.d.ts","sourceRoot":"","sources":["../../src/mobx-form/mobx-form.types.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,aAAa,EACb,YAAY,EACb,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,cAAc,CAC7B,YAAY,SAAS,WAAW,GAAG,WAAW,EAC9C,QAAQ,GAAG,GAAG,EACd,kBAAkB,GAAG,YAAY,CACjC,SAAQ,YAAY,CAAC,YAAY,EAAE,QAAQ,EAAE,kBAAkB,CAAC;IAChE;;OAEG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;OAEG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IAC7C;;OAEG;IACH,cAAc,CAAC,EAAE,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAClD;;OAEG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAED,MAAM,MAAM,sBAAsB,CAAC,CAAC,SAAS,WAAW,IAAI,OAAO,CACjE,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,EACrB,SAAS,GAAG,IAAI,CACjB,CAAC;AAEF,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,WAAW,IAAI,OAAO,CAC7D,CAAC,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,EACtB,SAAS,GAAG,IAAI,CACjB,CAAC;AAEF,MAAM,MAAM,4BAA4B,CAAC,CAAC,SAAS,WAAW,IAC5D,CAAC,SAAS,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC,GAClD,kBAAkB,GAClB,KAAK,CAAC"}
1
+ {"version":3,"file":"mobx-form.types.d.ts","sourceRoot":"","sources":["../../src/mobx-form/mobx-form.types.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,aAAa,EACb,YAAY,EACb,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,cAAc,CAC7B,YAAY,SAAS,WAAW,GAAG,WAAW,EAC9C,QAAQ,GAAG,GAAG,EACd,kBAAkB,GAAG,YAAY,CACjC,SAAQ,YAAY,CAAC,YAAY,EAAE,QAAQ,EAAE,kBAAkB,CAAC;IAChE;;OAEG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;OAEG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IAC7C;;OAEG;IACH,cAAc,CAAC,EAAE,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAClD;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,sBAAsB,CAAC,CAAC,SAAS,WAAW,IAAI,OAAO,CACjE,CAAC,CAAC,QAAQ,CAAC,EACX,SAAS,GAAG,IAAI,CACjB,CAAC;AAEF,MAAM,MAAM,4BAA4B,CAAC,CAAC,SAAS,WAAW,IAC5D,CAAC,SAAS,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC,GAClD,kBAAkB,GAClB,KAAK,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mobx-react-hook-form",
3
- "version": "2.1.3",
3
+ "version": "3.0.1",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "keywords": [],
@@ -22,6 +22,7 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "disposer-util": "^2.0.0",
25
+ "@types/react": "^18.3.1",
25
26
  "linked-abort-controller": "^1.1.0",
26
27
  "lodash-es": "^4.17.21",
27
28
  "yummies": "^3.0.39"
@@ -46,11 +47,6 @@
46
47
  "default": "./mobx-form/index.js",
47
48
  "types": "./mobx-form/index.d.ts"
48
49
  },
49
- "./mobx-form/mobx-form-state": {
50
- "import": "./mobx-form/mobx-form-state.js",
51
- "default": "./mobx-form/mobx-form-state.js",
52
- "types": "./mobx-form/mobx-form-state.d.ts"
53
- },
54
50
  "./mobx-form/mobx-form": {
55
51
  "import": "./mobx-form/mobx-form.js",
56
52
  "default": "./mobx-form/mobx-form.js",
@@ -1,28 +0,0 @@
1
- import { DeepMap, DeepPartial, FieldErrors, FieldValues, FormState } from 'react-hook-form';
2
- type FormValues<TFieldValues extends FieldValues> = {
3
- values: TFieldValues;
4
- };
5
- type MobxFormStateUpdate<TFieldValues extends FieldValues> = Omit<Partial<MobxFormState<TFieldValues>>, 'update'>;
6
- export type MobxFormStateReadonly<TFieldValues extends FieldValues> = Readonly<Omit<MobxFormState<TFieldValues>, 'update'>>;
7
- export declare class MobxFormState<TFieldValues extends FieldValues = FieldValues> implements FormState<TFieldValues>, FormValues<TFieldValues> {
8
- values: TFieldValues;
9
- isDirty: boolean;
10
- isLoading: boolean;
11
- isSubmitted: boolean;
12
- isSubmitSuccessful: boolean;
13
- isSubmitting: boolean;
14
- isValidating: boolean;
15
- isValid: boolean;
16
- disabled: boolean;
17
- submitCount: number;
18
- defaultValues: Readonly<DeepPartial<TFieldValues>> | undefined;
19
- dirtyFields: Partial<Readonly<DeepMap<DeepPartial<TFieldValues>, boolean>>>;
20
- touchedFields: Partial<Readonly<DeepMap<DeepPartial<TFieldValues>, boolean>>>;
21
- validatingFields: Partial<Readonly<DeepMap<DeepPartial<TFieldValues>, boolean>>>;
22
- errors: FieldErrors<TFieldValues>;
23
- isReady: boolean;
24
- constructor(initialState?: MobxFormStateUpdate<TFieldValues>);
25
- update({ values, errors, ...simpleProperties }: MobxFormStateUpdate<TFieldValues>): void;
26
- }
27
- export {};
28
- //# sourceMappingURL=mobx-form-state.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mobx-form-state.d.ts","sourceRoot":"","sources":["../../src/mobx-form/mobx-form-state.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,OAAO,EACP,WAAW,EACX,WAAW,EACX,WAAW,EACX,SAAS,EACV,MAAM,iBAAiB,CAAC;AAEzB,KAAK,UAAU,CAAC,YAAY,SAAS,WAAW,IAAI;IAClD,MAAM,EAAE,YAAY,CAAC;CACtB,CAAC;AAEF,KAAK,mBAAmB,CAAC,YAAY,SAAS,WAAW,IAAI,IAAI,CAC/D,OAAO,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,EACpC,QAAQ,CACT,CAAC;AAEF,MAAM,MAAM,qBAAqB,CAAC,YAAY,SAAS,WAAW,IAAI,QAAQ,CAC5E,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC,CAC5C,CAAC;AAEF,qBAAa,aAAa,CAAC,YAAY,SAAS,WAAW,GAAG,WAAW,CACvE,YAAW,SAAS,CAAC,YAAY,CAAC,EAAE,UAAU,CAAC,YAAY,CAAC;IAE5D,MAAM,EAAG,YAAY,CAAC;IACtB,OAAO,EAAE,OAAO,CAAS;IACzB,SAAS,EAAE,OAAO,CAAS;IAC3B,WAAW,EAAE,OAAO,CAAS;IAC7B,kBAAkB,EAAE,OAAO,CAAS;IACpC,YAAY,EAAE,OAAO,CAAS;IAC9B,YAAY,EAAE,OAAO,CAAS;IAC9B,OAAO,EAAE,OAAO,CAAS;IACzB,QAAQ,EAAE,OAAO,CAAS;IAC1B,WAAW,EAAE,MAAM,CAAK;IACxB,aAAa,EAAE,QAAQ,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,GAAG,SAAS,CAAC;IAC/D,WAAW,EAAE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CACtE;IACL,aAAa,EAAE,OAAO,CACpB,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC,CACtD,CAAM;IACP,gBAAgB,EAAE,OAAO,CACvB,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC,CACtD,CAAM;IACP,MAAM,EAAE,WAAW,CAAC,YAAY,CAAC,CAAM;IACvC,OAAO,EAAE,OAAO,CAAS;gBAEb,YAAY,CAAC,EAAE,mBAAmB,CAAC,YAAY,CAAC;IA0B5D,MAAM,CAAC,EACL,MAAM,EACN,MAAM,EACN,GAAG,gBAAgB,EACpB,EAAE,mBAAmB,CAAC,YAAY,CAAC;CAkCrC"}
@@ -1,76 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-unused-vars */
2
- /* eslint-disable @typescript-eslint/ban-ts-comment */
3
- import { action, makeObservable, observable } from 'mobx';
4
- export class MobxFormState {
5
- values;
6
- isDirty = false;
7
- isLoading = false;
8
- isSubmitted = false;
9
- isSubmitSuccessful = false;
10
- isSubmitting = false;
11
- isValidating = false;
12
- isValid = false;
13
- disabled = false;
14
- submitCount = 0;
15
- defaultValues;
16
- dirtyFields = {};
17
- touchedFields = {};
18
- validatingFields = {};
19
- errors = {};
20
- isReady = false;
21
- constructor(initialState) {
22
- if (initialState) {
23
- Object.assign(this, initialState);
24
- }
25
- observable.deep(this, 'values');
26
- observable.ref(this, 'isDirty');
27
- observable.ref(this, 'isLoading');
28
- observable.ref(this, 'isSubmitted');
29
- observable.ref(this, 'isSubmitSuccessful');
30
- observable.ref(this, 'isSubmitting');
31
- observable.ref(this, 'isValidating');
32
- observable.ref(this, 'isValid');
33
- observable.ref(this, 'disabled');
34
- observable.ref(this, 'submitCount');
35
- observable.ref(this, 'isReady');
36
- observable.deep(this, 'defaultValues');
37
- observable.deep(this, 'dirtyFields');
38
- observable.deep(this, 'touchedFields');
39
- observable.deep(this, 'validatingFields');
40
- observable.deep(this, 'errors');
41
- action(this, 'update');
42
- makeObservable(this);
43
- }
44
- update({ values, errors, ...simpleProperties }) {
45
- Object.entries(simpleProperties).forEach(([key, value]) => {
46
- if (value != null) {
47
- // @ts-ignore
48
- this[key] = value;
49
- }
50
- });
51
- if (errors) {
52
- const currentErrorsSet = new Set(Object.keys(this.errors));
53
- const newErrors = Object.keys(errors);
54
- for (const errorField of newErrors) {
55
- if (currentErrorsSet.has(errorField)) {
56
- currentErrorsSet.delete(errorField);
57
- // @ts-ignore
58
- Object.assign(this.errors[errorField], errors[errorField]);
59
- }
60
- else {
61
- // @ts-ignore
62
- this.errors[errorField] = errors[errorField];
63
- }
64
- }
65
- currentErrorsSet.forEach((errorField) => {
66
- // @ts-ignore
67
- delete this.errors[errorField];
68
- });
69
- }
70
- else {
71
- this.errors = {};
72
- }
73
- // @ts-ignore
74
- this.values = values ?? {};
75
- }
76
- }