remix-validated-form 4.0.0-beta.0 → 4.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.
@@ -1,9 +1,9 @@
1
1
  $ npm run build:browser && npm run build:main
2
2
 
3
- > remix-validated-form@3.4.2 build:browser
3
+ > remix-validated-form@4.0.1-beta.1 build:browser
4
4
  > tsc --module ESNext --outDir ./browser
5
5
 
6
6
 
7
- > remix-validated-form@3.4.2 build:main
7
+ > remix-validated-form@4.0.1-beta.1 build:main
8
8
  > tsc --module CommonJS --outDir ./build
9
9
 
@@ -47,4 +47,4 @@ export declare type FormProps<DataType> = {
47
47
  /**
48
48
  * The primary form component of `remix-validated-form`.
49
49
  */
50
- export declare function ValidatedForm<DataType>({ validator, onSubmit, children, fetcher, action, defaultValues, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, ...rest }: FormProps<DataType>): JSX.Element;
50
+ export declare function ValidatedForm<DataType>({ validator, onSubmit, children, fetcher, action, defaultValues, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, method, replace, ...rest }: FormProps<DataType>): JSX.Element;
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Form as RemixForm, useActionData, useFormAction, useSubmit, useTransition, } from "@remix-run/react";
2
+ import { Form as RemixForm, useActionData, useSubmit, useTransition, } from "@remix-run/react";
3
3
  import uniq from "lodash/uniq";
4
4
  import React, { useEffect, useMemo, useRef, useState, } from "react";
5
5
  import invariant from "tiny-invariant";
@@ -30,19 +30,16 @@ function useFieldErrors(fieldErrorsFromBackend) {
30
30
  }, [fieldErrorsFromBackend]);
31
31
  return [fieldErrors, setFieldErrors];
32
32
  }
33
- const useIsSubmitting = (action, subaction, fetcher) => {
34
- const actionForCurrentPage = useFormAction();
35
- const pendingFormSubmit = useTransition().submission;
36
- if (fetcher)
37
- return fetcher.state === "submitting";
38
- if (!pendingFormSubmit)
39
- return false;
40
- const { formData, action: pendingAction } = pendingFormSubmit;
41
- const pendingSubAction = formData.get("subaction");
42
- const expectedAction = action !== null && action !== void 0 ? action : actionForCurrentPage;
43
- if (subaction)
44
- return expectedAction === pendingAction && subaction === pendingSubAction;
45
- return expectedAction === pendingAction && !pendingSubAction;
33
+ const useIsSubmitting = (fetcher) => {
34
+ const [isSubmitStarted, setSubmitStarted] = useState(false);
35
+ const transition = useTransition();
36
+ const hasActiveSubmission = fetcher
37
+ ? fetcher.state === "submitting"
38
+ : !!transition.submission;
39
+ const isSubmitting = hasActiveSubmission && isSubmitStarted;
40
+ const startSubmit = () => setSubmitStarted(true);
41
+ const endSubmit = () => setSubmitStarted(false);
42
+ return [isSubmitting, startSubmit, endSubmit];
46
43
  };
47
44
  const getDataFromForm = (el) => new FormData(el);
48
45
  /**
@@ -110,11 +107,11 @@ const focusFirstInvalidInput = (fieldErrors, customFocusHandlers, formElement) =
110
107
  /**
111
108
  * The primary form component of `remix-validated-form`.
112
109
  */
113
- export function ValidatedForm({ validator, onSubmit, children, fetcher, action, defaultValues, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, ...rest }) {
110
+ export function ValidatedForm({ validator, onSubmit, children, fetcher, action, defaultValues, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, method, replace, ...rest }) {
114
111
  var _a;
115
112
  const backendError = useErrorResponseForThisForm(fetcher, subaction);
116
113
  const [fieldErrors, setFieldErrors] = useFieldErrors(backendError === null || backendError === void 0 ? void 0 : backendError.fieldErrors);
117
- const isSubmitting = useIsSubmitting(action, subaction, fetcher);
114
+ const [isSubmitting, startSubmit, endSubmit] = useIsSubmitting(fetcher);
118
115
  const defaultsToUse = useDefaultValues(backendError === null || backendError === void 0 ? void 0 : backendError.repopulateFields, defaultValues);
119
116
  const [touchedFields, setTouchedFields] = useState({});
120
117
  const [hasBeenSubmitted, setHasBeenSubmitted] = useState(false);
@@ -122,6 +119,7 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
122
119
  const formRef = useRef(null);
123
120
  useSubmitComplete(isSubmitting, () => {
124
121
  var _a;
122
+ endSubmit();
125
123
  if (!backendError && resetAfterSubmit) {
126
124
  (_a = formRef.current) === null || _a === void 0 ? void 0 : _a.reset();
127
125
  }
@@ -131,7 +129,7 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
131
129
  fieldErrors,
132
130
  action,
133
131
  defaultValues: defaultsToUse,
134
- isSubmitting: isSubmitting !== null && isSubmitting !== void 0 ? isSubmitting : false,
132
+ isSubmitting,
135
133
  isValid: Object.keys(fieldErrors).length === 0,
136
134
  touchedFields,
137
135
  setFieldTouched: (fieldName, touched) => setTouchedFields((prev) => ({
@@ -205,11 +203,13 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
205
203
  window.removeEventListener("click", handleClick);
206
204
  };
207
205
  }, []);
208
- return (_jsx(Form, { ref: mergeRefs([formRef, formRefProp]), ...rest, action: action, onSubmit: async (e) => {
206
+ return (_jsx(Form, { ref: mergeRefs([formRef, formRefProp]), ...rest, action: action, method: method, replace: replace, onSubmit: async (e) => {
209
207
  e.preventDefault();
210
208
  setHasBeenSubmitted(true);
209
+ startSubmit();
211
210
  const result = await validator.validate(getDataFromForm(e.currentTarget));
212
211
  if (result.error) {
212
+ endSubmit();
213
213
  setFieldErrors(result.error.fieldErrors);
214
214
  if (!disableFocusOnError) {
215
215
  focusFirstInvalidInput(result.error.fieldErrors, customFocusHandlers(), formRef.current);
@@ -220,7 +220,10 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
220
220
  if (fetcher)
221
221
  fetcher.submit(clickedButtonRef.current || e.currentTarget);
222
222
  else
223
- submit(clickedButtonRef.current || e.currentTarget);
223
+ submit(clickedButtonRef.current || e.currentTarget, {
224
+ method,
225
+ replace,
226
+ });
224
227
  clickedButtonRef.current = null;
225
228
  }
226
229
  }, onReset: (event) => {
@@ -1,5 +1,4 @@
1
1
  import { GetInputProps, ValidationBehaviorOptions } from "./internal/getInputProps";
2
- import { ValidationState } from "./types";
3
2
  export declare type FieldProps = {
4
3
  /**
5
4
  * The validation error message if there is one.
@@ -13,14 +12,6 @@ export declare type FieldProps = {
13
12
  * Validates the field.
14
13
  */
15
14
  validate: () => void;
16
- /**
17
- * The validation state of the field.
18
- * - idle: the field has not been validated yet.
19
- * - validating: the field is currently being validated.
20
- * - valid: the field is valid.
21
- * - invalid: the field is invalid.
22
- */
23
- validationState: ValidationState;
24
15
  /**
25
16
  * The default value of the field, if there is one.
26
17
  */
package/browser/hooks.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import get from "lodash/get";
2
2
  import toPath from "lodash/toPath";
3
- import { useContext, useEffect, useMemo, useState } from "react";
3
+ import { useContext, useEffect, useMemo } from "react";
4
4
  import { FormContext } from "./internal/formContext";
5
5
  import { createGetInputProps, } from "./internal/getInputProps";
6
6
  const useInternalFormContext = (hookName) => {
@@ -16,37 +16,24 @@ export const useField = (name, options) => {
16
16
  const { fieldErrors, clearError, validateField, defaultValues, registerReceiveFocus, touchedFields, setFieldTouched, hasBeenSubmitted, } = useInternalFormContext("useField");
17
17
  const isTouched = !!touchedFields[name];
18
18
  const { handleReceiveFocus } = options !== null && options !== void 0 ? options : {};
19
- const [isValidating, setValidating] = useState(false);
20
19
  useEffect(() => {
21
20
  if (handleReceiveFocus)
22
21
  return registerReceiveFocus(name, handleReceiveFocus);
23
22
  }, [handleReceiveFocus, name, registerReceiveFocus]);
24
23
  const field = useMemo(() => {
25
- const error = fieldErrors[name];
26
- const getValidationState = () => {
27
- if (isValidating)
28
- return "validating";
29
- if (error)
30
- return "invalid";
31
- if (!isTouched && !hasBeenSubmitted)
32
- return "idle";
33
- return "valid";
34
- };
35
24
  const helpers = {
36
- error,
25
+ error: fieldErrors[name],
37
26
  clearError: () => {
38
27
  clearError(name);
39
28
  },
40
29
  validate: () => {
41
- setValidating(true);
42
- validateField(name).then((error) => setValidating(false));
30
+ validateField(name);
43
31
  },
44
32
  defaultValue: defaultValues
45
33
  ? get(defaultValues, toPath(name), undefined)
46
34
  : undefined,
47
35
  touched: isTouched,
48
36
  setTouched: (touched) => setFieldTouched(name, touched),
49
- validationState: getValidationState(),
50
37
  };
51
38
  const getInputProps = createGetInputProps({
52
39
  ...helpers,
@@ -65,7 +52,6 @@ export const useField = (name, options) => {
65
52
  isTouched,
66
53
  hasBeenSubmitted,
67
54
  options === null || options === void 0 ? void 0 : options.validationBehavior,
68
- isValidating,
69
55
  clearError,
70
56
  validateField,
71
57
  setFieldTouched,
@@ -4,7 +4,7 @@ import { ValidatorError } from "./validation/types";
4
4
  * When you return this from your action, `ValidatedForm` on the frontend will automatically
5
5
  * display the errors on the correct fields on the correct form.
6
6
  *
7
- * _Recommended_: You can also provide a second argument to `validationError`
7
+ * You can also provide a second argument to `validationError`
8
8
  * to specify how to repopulate the form when JS is disabled.
9
9
  *
10
10
  * @example
package/browser/server.js CHANGED
@@ -4,7 +4,7 @@ import { json } from "@remix-run/server-runtime";
4
4
  * When you return this from your action, `ValidatedForm` on the frontend will automatically
5
5
  * display the errors on the correct fields on the correct form.
6
6
  *
7
- * _Recommended_: You can also provide a second argument to `validationError`
7
+ * You can also provide a second argument to `validationError`
8
8
  * to specify how to repopulate the form when JS is disabled.
9
9
  *
10
10
  * @example
@@ -47,4 +47,4 @@ export declare type FormProps<DataType> = {
47
47
  /**
48
48
  * The primary form component of `remix-validated-form`.
49
49
  */
50
- export declare function ValidatedForm<DataType>({ validator, onSubmit, children, fetcher, action, defaultValues, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, ...rest }: FormProps<DataType>): JSX.Element;
50
+ export declare function ValidatedForm<DataType>({ validator, onSubmit, children, fetcher, action, defaultValues, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, method, replace, ...rest }: FormProps<DataType>): JSX.Element;
@@ -55,19 +55,16 @@ function useFieldErrors(fieldErrorsFromBackend) {
55
55
  }, [fieldErrorsFromBackend]);
56
56
  return [fieldErrors, setFieldErrors];
57
57
  }
58
- const useIsSubmitting = (action, subaction, fetcher) => {
59
- const actionForCurrentPage = (0, react_1.useFormAction)();
60
- const pendingFormSubmit = (0, react_1.useTransition)().submission;
61
- if (fetcher)
62
- return fetcher.state === "submitting";
63
- if (!pendingFormSubmit)
64
- return false;
65
- const { formData, action: pendingAction } = pendingFormSubmit;
66
- const pendingSubAction = formData.get("subaction");
67
- const expectedAction = action !== null && action !== void 0 ? action : actionForCurrentPage;
68
- if (subaction)
69
- return expectedAction === pendingAction && subaction === pendingSubAction;
70
- return expectedAction === pendingAction && !pendingSubAction;
58
+ const useIsSubmitting = (fetcher) => {
59
+ const [isSubmitStarted, setSubmitStarted] = (0, react_2.useState)(false);
60
+ const transition = (0, react_1.useTransition)();
61
+ const hasActiveSubmission = fetcher
62
+ ? fetcher.state === "submitting"
63
+ : !!transition.submission;
64
+ const isSubmitting = hasActiveSubmission && isSubmitStarted;
65
+ const startSubmit = () => setSubmitStarted(true);
66
+ const endSubmit = () => setSubmitStarted(false);
67
+ return [isSubmitting, startSubmit, endSubmit];
71
68
  };
72
69
  const getDataFromForm = (el) => new FormData(el);
73
70
  /**
@@ -135,11 +132,11 @@ const focusFirstInvalidInput = (fieldErrors, customFocusHandlers, formElement) =
135
132
  /**
136
133
  * The primary form component of `remix-validated-form`.
137
134
  */
138
- function ValidatedForm({ validator, onSubmit, children, fetcher, action, defaultValues, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, ...rest }) {
135
+ function ValidatedForm({ validator, onSubmit, children, fetcher, action, defaultValues, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, method, replace, ...rest }) {
139
136
  var _a;
140
137
  const backendError = useErrorResponseForThisForm(fetcher, subaction);
141
138
  const [fieldErrors, setFieldErrors] = useFieldErrors(backendError === null || backendError === void 0 ? void 0 : backendError.fieldErrors);
142
- const isSubmitting = useIsSubmitting(action, subaction, fetcher);
139
+ const [isSubmitting, startSubmit, endSubmit] = useIsSubmitting(fetcher);
143
140
  const defaultsToUse = useDefaultValues(backendError === null || backendError === void 0 ? void 0 : backendError.repopulateFields, defaultValues);
144
141
  const [touchedFields, setTouchedFields] = (0, react_2.useState)({});
145
142
  const [hasBeenSubmitted, setHasBeenSubmitted] = (0, react_2.useState)(false);
@@ -147,6 +144,7 @@ function ValidatedForm({ validator, onSubmit, children, fetcher, action, default
147
144
  const formRef = (0, react_2.useRef)(null);
148
145
  (0, submissionCallbacks_1.useSubmitComplete)(isSubmitting, () => {
149
146
  var _a;
147
+ endSubmit();
150
148
  if (!backendError && resetAfterSubmit) {
151
149
  (_a = formRef.current) === null || _a === void 0 ? void 0 : _a.reset();
152
150
  }
@@ -156,7 +154,7 @@ function ValidatedForm({ validator, onSubmit, children, fetcher, action, default
156
154
  fieldErrors,
157
155
  action,
158
156
  defaultValues: defaultsToUse,
159
- isSubmitting: isSubmitting !== null && isSubmitting !== void 0 ? isSubmitting : false,
157
+ isSubmitting,
160
158
  isValid: Object.keys(fieldErrors).length === 0,
161
159
  touchedFields,
162
160
  setFieldTouched: (fieldName, touched) => setTouchedFields((prev) => ({
@@ -230,11 +228,13 @@ function ValidatedForm({ validator, onSubmit, children, fetcher, action, default
230
228
  window.removeEventListener("click", handleClick);
231
229
  };
232
230
  }, []);
233
- return ((0, jsx_runtime_1.jsx)(Form, { ref: (0, util_1.mergeRefs)([formRef, formRefProp]), ...rest, action: action, onSubmit: async (e) => {
231
+ return ((0, jsx_runtime_1.jsx)(Form, { ref: (0, util_1.mergeRefs)([formRef, formRefProp]), ...rest, action: action, method: method, replace: replace, onSubmit: async (e) => {
234
232
  e.preventDefault();
235
233
  setHasBeenSubmitted(true);
234
+ startSubmit();
236
235
  const result = await validator.validate(getDataFromForm(e.currentTarget));
237
236
  if (result.error) {
237
+ endSubmit();
238
238
  setFieldErrors(result.error.fieldErrors);
239
239
  if (!disableFocusOnError) {
240
240
  focusFirstInvalidInput(result.error.fieldErrors, customFocusHandlers(), formRef.current);
@@ -245,7 +245,10 @@ function ValidatedForm({ validator, onSubmit, children, fetcher, action, default
245
245
  if (fetcher)
246
246
  fetcher.submit(clickedButtonRef.current || e.currentTarget);
247
247
  else
248
- submit(clickedButtonRef.current || e.currentTarget);
248
+ submit(clickedButtonRef.current || e.currentTarget, {
249
+ method,
250
+ replace,
251
+ });
249
252
  clickedButtonRef.current = null;
250
253
  }
251
254
  }, onReset: (event) => {
package/build/hooks.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { GetInputProps, ValidationBehaviorOptions } from "./internal/getInputProps";
2
- import { ValidationState } from "./types";
3
2
  export declare type FieldProps = {
4
3
  /**
5
4
  * The validation error message if there is one.
@@ -13,14 +12,6 @@ export declare type FieldProps = {
13
12
  * Validates the field.
14
13
  */
15
14
  validate: () => void;
16
- /**
17
- * The validation state of the field.
18
- * - idle: the field has not been validated yet.
19
- * - validating: the field is currently being validated.
20
- * - valid: the field is valid.
21
- * - invalid: the field is invalid.
22
- */
23
- validationState: ValidationState;
24
15
  /**
25
16
  * The default value of the field, if there is one.
26
17
  */
package/build/hooks.js CHANGED
@@ -22,37 +22,24 @@ const useField = (name, options) => {
22
22
  const { fieldErrors, clearError, validateField, defaultValues, registerReceiveFocus, touchedFields, setFieldTouched, hasBeenSubmitted, } = useInternalFormContext("useField");
23
23
  const isTouched = !!touchedFields[name];
24
24
  const { handleReceiveFocus } = options !== null && options !== void 0 ? options : {};
25
- const [isValidating, setValidating] = (0, react_1.useState)(false);
26
25
  (0, react_1.useEffect)(() => {
27
26
  if (handleReceiveFocus)
28
27
  return registerReceiveFocus(name, handleReceiveFocus);
29
28
  }, [handleReceiveFocus, name, registerReceiveFocus]);
30
29
  const field = (0, react_1.useMemo)(() => {
31
- const error = fieldErrors[name];
32
- const getValidationState = () => {
33
- if (isValidating)
34
- return "validating";
35
- if (error)
36
- return "invalid";
37
- if (!isTouched && !hasBeenSubmitted)
38
- return "idle";
39
- return "valid";
40
- };
41
30
  const helpers = {
42
- error,
31
+ error: fieldErrors[name],
43
32
  clearError: () => {
44
33
  clearError(name);
45
34
  },
46
35
  validate: () => {
47
- setValidating(true);
48
- validateField(name).then((error) => setValidating(false));
36
+ validateField(name);
49
37
  },
50
38
  defaultValue: defaultValues
51
39
  ? (0, get_1.default)(defaultValues, (0, toPath_1.default)(name), undefined)
52
40
  : undefined,
53
41
  touched: isTouched,
54
42
  setTouched: (touched) => setFieldTouched(name, touched),
55
- validationState: getValidationState(),
56
43
  };
57
44
  const getInputProps = (0, getInputProps_1.createGetInputProps)({
58
45
  ...helpers,
@@ -71,7 +58,6 @@ const useField = (name, options) => {
71
58
  isTouched,
72
59
  hasBeenSubmitted,
73
60
  options === null || options === void 0 ? void 0 : options.validationBehavior,
74
- isValidating,
75
61
  clearError,
76
62
  validateField,
77
63
  setFieldTouched,
package/build/server.d.ts CHANGED
@@ -4,7 +4,7 @@ import { ValidatorError } from "./validation/types";
4
4
  * When you return this from your action, `ValidatedForm` on the frontend will automatically
5
5
  * display the errors on the correct fields on the correct form.
6
6
  *
7
- * _Recommended_: You can also provide a second argument to `validationError`
7
+ * You can also provide a second argument to `validationError`
8
8
  * to specify how to repopulate the form when JS is disabled.
9
9
  *
10
10
  * @example
package/build/server.js CHANGED
@@ -7,7 +7,7 @@ const server_runtime_1 = require("@remix-run/server-runtime");
7
7
  * When you return this from your action, `ValidatedForm` on the frontend will automatically
8
8
  * display the errors on the correct fields on the correct form.
9
9
  *
10
- * _Recommended_: You can also provide a second argument to `validationError`
10
+ * You can also provide a second argument to `validationError`
11
11
  * to specify how to repopulate the form when JS is disabled.
12
12
  *
13
13
  * @example
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remix-validated-form",
3
- "version": "4.0.0-beta.0",
3
+ "version": "4.0.1",
4
4
  "description": "Form component and utils for easy form validation in remix",
5
5
  "browser": "./browser/index.js",
6
6
  "main": "./build/index.js",
@@ -6,6 +6,7 @@ import {
6
6
  useSubmit,
7
7
  useTransition,
8
8
  } from "@remix-run/react";
9
+ import { Fetcher } from "@remix-run/react/transition";
9
10
  import uniq from "lodash/uniq";
10
11
  import React, {
11
12
  ComponentProps,
@@ -106,22 +107,19 @@ function useFieldErrors(
106
107
  }
107
108
 
108
109
  const useIsSubmitting = (
109
- action?: string,
110
- subaction?: string,
111
- fetcher?: ReturnType<typeof useFetcher>
112
- ) => {
113
- const actionForCurrentPage = useFormAction();
114
- const pendingFormSubmit = useTransition().submission;
110
+ fetcher?: Fetcher
111
+ ): [boolean, () => void, () => void] => {
112
+ const [isSubmitStarted, setSubmitStarted] = useState(false);
113
+ const transition = useTransition();
114
+ const hasActiveSubmission = fetcher
115
+ ? fetcher.state === "submitting"
116
+ : !!transition.submission;
117
+ const isSubmitting = hasActiveSubmission && isSubmitStarted;
115
118
 
116
- if (fetcher) return fetcher.state === "submitting";
117
- if (!pendingFormSubmit) return false;
119
+ const startSubmit = () => setSubmitStarted(true);
120
+ const endSubmit = () => setSubmitStarted(false);
118
121
 
119
- const { formData, action: pendingAction } = pendingFormSubmit;
120
- const pendingSubAction = formData.get("subaction");
121
- const expectedAction = action ?? actionForCurrentPage;
122
- if (subaction)
123
- return expectedAction === pendingAction && subaction === pendingSubAction;
124
- return expectedAction === pendingAction && !pendingSubAction;
122
+ return [isSubmitting, startSubmit, endSubmit];
125
123
  };
126
124
 
127
125
  const getDataFromForm = (el: HTMLFormElement) => new FormData(el);
@@ -218,13 +216,16 @@ export function ValidatedForm<DataType>({
218
216
  subaction,
219
217
  resetAfterSubmit,
220
218
  disableFocusOnError,
219
+ method,
220
+ replace,
221
221
  ...rest
222
222
  }: FormProps<DataType>) {
223
223
  const backendError = useErrorResponseForThisForm(fetcher, subaction);
224
224
  const [fieldErrors, setFieldErrors] = useFieldErrors(
225
225
  backendError?.fieldErrors
226
226
  );
227
- const isSubmitting = useIsSubmitting(action, subaction, fetcher);
227
+ const [isSubmitting, startSubmit, endSubmit] = useIsSubmitting(fetcher);
228
+
228
229
  const defaultsToUse = useDefaultValues(
229
230
  backendError?.repopulateFields,
230
231
  defaultValues
@@ -234,6 +235,7 @@ export function ValidatedForm<DataType>({
234
235
  const submit = useSubmit();
235
236
  const formRef = useRef<HTMLFormElement>(null);
236
237
  useSubmitComplete(isSubmitting, () => {
238
+ endSubmit();
237
239
  if (!backendError && resetAfterSubmit) {
238
240
  formRef.current?.reset();
239
241
  }
@@ -245,7 +247,7 @@ export function ValidatedForm<DataType>({
245
247
  fieldErrors,
246
248
  action,
247
249
  defaultValues: defaultsToUse,
248
- isSubmitting: isSubmitting ?? false,
250
+ isSubmitting,
249
251
  isValid: Object.keys(fieldErrors).length === 0,
250
252
  touchedFields,
251
253
  setFieldTouched: (fieldName: string, touched: boolean) =>
@@ -336,13 +338,17 @@ export function ValidatedForm<DataType>({
336
338
  ref={mergeRefs([formRef, formRefProp])}
337
339
  {...rest}
338
340
  action={action}
341
+ method={method}
342
+ replace={replace}
339
343
  onSubmit={async (e) => {
340
344
  e.preventDefault();
341
345
  setHasBeenSubmitted(true);
346
+ startSubmit();
342
347
  const result = await validator.validate(
343
348
  getDataFromForm(e.currentTarget)
344
349
  );
345
350
  if (result.error) {
351
+ endSubmit();
346
352
  setFieldErrors(result.error.fieldErrors);
347
353
  if (!disableFocusOnError) {
348
354
  focusFirstInvalidInput(
@@ -355,7 +361,11 @@ export function ValidatedForm<DataType>({
355
361
  onSubmit && onSubmit(result.data, e);
356
362
  if (fetcher)
357
363
  fetcher.submit(clickedButtonRef.current || e.currentTarget);
358
- else submit(clickedButtonRef.current || e.currentTarget);
364
+ else
365
+ submit(clickedButtonRef.current || e.currentTarget, {
366
+ method,
367
+ replace,
368
+ });
359
369
  clickedButtonRef.current = null;
360
370
  }
361
371
  }}
package/src/hooks.ts CHANGED
@@ -1,13 +1,12 @@
1
1
  import get from "lodash/get";
2
2
  import toPath from "lodash/toPath";
3
- import { useContext, useEffect, useMemo, useState } from "react";
3
+ import { useContext, useEffect, useMemo } from "react";
4
4
  import { FormContext } from "./internal/formContext";
5
5
  import {
6
6
  createGetInputProps,
7
7
  GetInputProps,
8
8
  ValidationBehaviorOptions,
9
9
  } from "./internal/getInputProps";
10
- import { ValidationState } from "./types";
11
10
 
12
11
  const useInternalFormContext = (hookName: string) => {
13
12
  const context = useContext(FormContext);
@@ -31,14 +30,6 @@ export type FieldProps = {
31
30
  * Validates the field.
32
31
  */
33
32
  validate: () => void;
34
- /**
35
- * The validation state of the field.
36
- * - idle: the field has not been validated yet.
37
- * - validating: the field is currently being validated.
38
- * - valid: the field is valid.
39
- * - invalid: the field is invalid.
40
- */
41
- validationState: ValidationState;
42
33
  /**
43
34
  * The default value of the field, if there is one.
44
35
  */
@@ -88,7 +79,6 @@ export const useField = (
88
79
 
89
80
  const isTouched = !!touchedFields[name];
90
81
  const { handleReceiveFocus } = options ?? {};
91
- const [isValidating, setValidating] = useState(false);
92
82
 
93
83
  useEffect(() => {
94
84
  if (handleReceiveFocus)
@@ -96,28 +86,19 @@ export const useField = (
96
86
  }, [handleReceiveFocus, name, registerReceiveFocus]);
97
87
 
98
88
  const field = useMemo<FieldProps>(() => {
99
- const error = fieldErrors[name];
100
- const getValidationState = (): ValidationState => {
101
- if (isValidating) return "validating";
102
- if (error) return "invalid";
103
- if (!isTouched && !hasBeenSubmitted) return "idle";
104
- return "valid";
105
- };
106
89
  const helpers = {
107
- error,
90
+ error: fieldErrors[name],
108
91
  clearError: () => {
109
92
  clearError(name);
110
93
  },
111
94
  validate: () => {
112
- setValidating(true);
113
- validateField(name).then((error) => setValidating(false));
95
+ validateField(name);
114
96
  },
115
97
  defaultValue: defaultValues
116
98
  ? get(defaultValues, toPath(name), undefined)
117
99
  : undefined,
118
100
  touched: isTouched,
119
101
  setTouched: (touched: boolean) => setFieldTouched(name, touched),
120
- validationState: getValidationState(),
121
102
  };
122
103
  const getInputProps = createGetInputProps({
123
104
  ...helpers,
@@ -136,7 +117,6 @@ export const useField = (
136
117
  isTouched,
137
118
  hasBeenSubmitted,
138
119
  options?.validationBehavior,
139
- isValidating,
140
120
  clearError,
141
121
  validateField,
142
122
  setFieldTouched,
package/src/server.ts CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  * When you return this from your action, `ValidatedForm` on the frontend will automatically
10
10
  * display the errors on the correct fields on the correct form.
11
11
  *
12
- * _Recommended_: You can also provide a second argument to `validationError`
12
+ * You can also provide a second argument to `validationError`
13
13
  * to specify how to repopulate the form when JS is disabled.
14
14
  *
15
15
  * @example
package/src/types.ts DELETED
@@ -1 +0,0 @@
1
- export type ValidationState = "idle" | "validating" | "valid" | "invalid";