T
TanStack•3w ago
other-emerald

How to fields disable when form submitting?

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>
<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>
12 Replies
absent-sapphire
absent-sapphire•3w ago
This is my reusable code
Are you using form composition, or are you passing this in the callback?
foreign-sapphire
foreign-sapphire•3w ago
isSubmitting flag
absent-sapphire
absent-sapphire•3w ago
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>
// rerender whenever isSubmitting changes
const isSubmitting = useStore(form.store, state => state.isSubmitting)

<form.Subscribe selector={state => state.isSubmitting}>
{isSubmitting => <></>}
</form.Subscribe>
other-emerald
other-emeraldOP•3w ago
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>
);
}
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>
);
}
absent-sapphire
absent-sapphire•3w ago
Form Composition | TanStack Form React Docs
A common criticism of TanStack Form is its verbosity out-of-the-box. While this can be useful for educational purposes helping enforce understanding our APIs it's not ideal in production use cases. As...
other-emerald
other-emeraldOP•3w ago
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>
);
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?
absent-sapphire
absent-sapphire•3w ago
it's not type safe, so I'll recommend the docs I sent. Anyways, your forum post's about the isSubmitting part, so that can be solved by the comment I sent here: 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
other-emerald
other-emeraldOP•3w ago
Yes, it go to isTouched: false -> isTouched: true. is it related to isSubmitting and not to update?
absent-sapphire
absent-sapphire•3w ago
isSubmitting does not cause rerenders on its own. You need to subscribe to it. 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>
<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>
other-emerald
other-emeraldOP•2w ago
ok i will check it thank you very much
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>
);
}
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
absent-sapphire
absent-sapphire•2w ago
isSubmitting - Is the form currently in the process of submitting isSubmitted - The form has successfully submitted
other-emerald
other-emeraldOP•2w ago
Yes, i need to disable input field while submiting, but above code not work 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>
</>
);
}
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. 🥹

Did you find this page helpful?