#How to fields disable when form submitting?

29 messages · Page 1 of 1 (latest)

cobalt birch
#

This is my reusable code and i try to add disable when submitting but it is not working. It update as true when submit click but not update again to false when validation error or submit finished.

I used this code to check it - {"Subminiting?:" + (field.form.state.isSubmitting === true ? "Yes" : "No")}

<Field
      data-invalid={field.state.meta.isTouched && !field.state.meta.isValid}
    >
      {label && <FieldLabel htmlFor={field.name}>{label || ""}</FieldLabel>}
      <Input
        value={field.state.value}
        type={type || "text"}
        name={field.name}
        id={field.name}
        placeholder={placeholder || ""}
        autoComplete={autoComplete}
        required={required ?? true}
        onBlur={field.handleBlur}
        onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
          field.handleChange(e.target.value)
        }
        {...others}
        disabled={field.form.state.isSubmitting}
      />
      {"Subminiting?:" +
        (field.form.state.isSubmitting === true ? "Yes" : "No")}
      {console.log(field.form)}
      {description && <FieldDescription>{description}</FieldDescription>}
      {children}
      <FieldInfo field={field} />
    </Field>```
fossil current
tall cloud
#

isSubmitting flag

fossil current
#

the flag's correct, but it's likely not reactive on its own. You'll want to use one of the two ways to get a reactive value

// rerender whenever isSubmitting changes
const isSubmitting = useStore(form.store, state => state.isSubmitting)

<form.Subscribe selector={state => state.isSubmitting}>
  {isSubmitting => <></>}
</form.Subscribe>
cobalt birch
# fossil current > This is my reusable code Are you using form composition, or are you passing th...

This is form component for reuse.

export default function Form(
  props: PropsWithChildren<
    {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      form: any;
      className?: string;
      onSubmit?: React.FormEventHandler<HTMLFormElement>;
    } & React.FormHTMLAttributes<HTMLFormElement>
  >
) {
  const { children, form, className, onSubmit, ...rest } = props as any;
  return (
    <FormContext.Provider value={form}>
      <form
        noValidate
        className={className || "space-y-4"}
        onSubmit={(e) => {
          e.preventDefault();
          e.stopPropagation();
          if (onSubmit) onSubmit(e);
          if (form?.handleSubmit) form.handleSubmit(); // { submitAction: "continue" }
        }}
        {...rest}
      >
        <FieldGroup>{children}</FieldGroup>
      </form>
    </FormContext.Provider>
  );
}```
fossil current
cobalt birch
#

I use that form compoent and in between <Form></Form> I use FeildInput Component. I can not add full code to this becuase it ask to money.

return (
    <form.Field name={name} validators={validators} listeners={listeners || {}}>
      {array
        ? (subField: any) => renderComponent(subField)
        : (field: any) => renderComponent(field)}
    </form.Field>
  );```

This renderComponent is above first code and it is inside this ` const renderComponent = (field: any) => ( ABOVE CODE FIST CODE WHEN ASK QUESTION)`. Is this wrong?
fossil current
#

You said that it worked from false -> true, but not true -> false. That was a coincidence. It actually rerendered because the field has gone from isTouched: false -> isTouched: true

cobalt birch
fossil current
#

It would look something like:

<field.form.Subscribe selector={state => state.isSubmitting}>
  {isSubmitting => (
   <Input
        value={field.state.value}
        type={type || "text"}
        name={field.name}
        id={field.name}
        placeholder={placeholder || ""}
        autoComplete={autoComplete}
        required={required ?? true}
        onBlur={field.handleBlur}
        onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
          field.handleChange(e.target.value)
        }
        {...others}
        disabled={isSubmitting}
      />
  )}
</field.form.Subscribe>
cobalt birch
#

ok i will check it

#

thank you very much

cobalt birch
# fossil current It would look something like: ```tsx <field.form.Subscribe selector={state => st...
 const field = useFieldContext();
  const isSubmitting = useStore(
    field.form.store,
    (state) => state.isSubmitting
  );
  return (
    <Field
      data-invalid={field.state.meta.isTouched && !field.state.meta.isValid}
    >
      {label && <FieldLabel htmlFor={field.name}>{label}</FieldLabel>}

      <Input
        value={field.state.value}
        type={type || "text"}
        name={field.name}
        id={field.name}
        placeholder={placeholder || ""}
        autoComplete={autoComplete}
        required={required ?? true}
        aria-invalid={field.state.meta.isTouched && !field.state.meta.isValid}
        onBlur={field.handleBlur}
        onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
          field.handleChange(e.target.value)
        }
        disabled={isSubmitting}
        {...others}
      />
      {"isSubmitting? " + isSubmitting}
      {description && <FieldDescription>{description}</FieldDescription>}
      {children}
      <FieldInfo field={field} />
    </Field>
  );
}```
#

Why this code not work?

#

I always get false for {"isSubmitting? " + isSubmitting} but form is submited

fossil current
cobalt birch
#

is that because i used const field = useFieldContext(); ?

#

I mean during the form submitting progress, text field not disabled. Always show false even submitting process in {"isSubmitting? " + isSubmitting}.

#
const { useAppForm, withForm } = createFormHook({
  fieldComponents: {
    TextField,
  },
  formComponents: {
    SubscribeButton,
  },
  fieldContext,
  formContext,
});

const formOpts = formOptions({
  defaultValues: {
    firstName: "John",
    lastName: "Doe",
  },
});

const ChildForm = withForm({
  ...formOpts,
  // Optional, but adds props to the `render` function outside of `form`
  props: {
    title: "Child Form",
  },
  render: ({ form, title }) => {
    return (
      <div>
        <p>{title}</p>
        <form.AppField
          name="firstName"
          children={(field) => (
            <field.TextField label="First Name" name="firstName" />
          )}
        />
        <form.AppForm>
          <form.SubscribeButton label="Submit" />
        </form.AppForm>
      </div>
    );
  },
});

function RouteComponent() {
  const form = useAppForm({
    ...formOpts,
    onSubmit: ({ value }) => {
      // Add artificial delay to simulate form submission
      setTimeout(() => {
        console.log(value);
      }, 10000);
    },
  });

  return (
    <>
      <form
        id="bug-report-form"
        onSubmit={(e) => {
          e.preventDefault();
          // e.stopPropagation();
          form.handleSubmit(e);
        }}
      >
        <h1>Form Check</h1>
        <ChildForm form={form} title={"Testing"} />
      </form>
    </>
  );
}```
#

I found the error, field.form.store.state.isSubmitting update quickly but values will delay. I do not know why.

When use {console.log(field.form.store.state.isSubmitting)} console see false true false quickly but values print in delay after 10000.

#

Why isSubmitting is update quickly? isSubmitting should true until 10000 delay.

#

I found the solution it should be async delay. 🥹

crisp flicker
#

hey sers, what is the move "tanstack" way to use form + query ?
We have a debate on what should we use to mark submitting button form as disabled.
Should we use isSubmitting hence we have to use return a promise in onSubmit and use a try catch here
Or use the mutate state isPending and use callback for onError and such
Best !

fossil current
#

isSubmitting is reachable anywhere where there's a form available, so it works well with form components like a submit button.

If you're always explicit about it and prefer it that way, then mutation.isPending works just fine too

crisp flicker
#

Yes the only gotcha for me with mutateAsync is that onError callback sill throw the error in promise. we try to avoid try catch as much as possible 🙁