T
TanStackโ€ข2y ago
defeated-apricot

how to disable submit button initially?

Apparently, canSubmit is true initially even if e.g. mandatory fields are empty, because no field validation happened yet. I tried using defaultState like this, but it did not work:
const form = useForm({
defaultValues: {
firstName: "",
lastName: "",
},
defaultState: {
canSubmit: false,
},
onSubmit: async (values) => {
console.log(values);
},
});
const form = useForm({
defaultValues: {
firstName: "",
lastName: "",
},
defaultState: {
canSubmit: false,
},
onSubmit: async (values) => {
console.log(values);
},
});
I can disable my submit button using something like disabled={!(isTouched && canSubmit)} but is there a "better" way?
20 Replies
wise-white
wise-whiteโ€ข14mo ago
I have exactly the same issue and I am confused. The behavior still the same, an year later. Is it intentional?
deep-jade
deep-jadeโ€ข14mo ago
Maybe too late but that's how I did it, I check if the form didn't got "touched" with isTouched
<form.Subscribe
selector={(state) => [
state.canSubmit,
state.isSubmitting,
state.isTouched,
]}
children={([canSubmit, isSubmitting, isTouched]) => (
<Button
type="submit"
disabled={!canSubmit || !isTouched}
className="w-fit rounded-sm"
color="primary-dark"
>
{isSubmitting ? "..." : "Save"}
</Button>
)}
/>

<form.Subscribe
selector={(state) => [
state.canSubmit,
state.isSubmitting,
state.isTouched,
]}
children={([canSubmit, isSubmitting, isTouched]) => (
<Button
type="submit"
disabled={!canSubmit || !isTouched}
className="w-fit rounded-sm"
color="primary-dark"
>
{isSubmitting ? "..." : "Save"}
</Button>
)}
/>

correct-apricot
correct-apricotโ€ข13mo ago
Also hitting this and opened https://github.com/TanStack/form/pull/909
GitHub
canSubmit with onMount validators by oscartbeaumont ยท Pull Requ...
Stackblitz reproduction. Notice how the &quot;Submit&quot; button is not disabled, but the onMount validator returned an error (as shown next to the field). I would expect canSubmit to retu...
wise-white
wise-whiteโ€ข2mo ago
you have access to form.state.isPristine, which informs you whether the form has been changed or not. Is that what you are looking for? disabled={isPristine || !canSubmit}
manual-pink
manual-pinkโ€ข2mo ago
Yeah that can be a sollution, thanks! But I still think this is not ideal, what do you think?
wise-white
wise-whiteโ€ข2mo ago
I still think this is not ideal
That the default for canSubmit is true? Or that you can't configure the initial state of it?
manual-pink
manual-pinkโ€ข2mo ago
Yes, the default value for canSubmit is true even if the validation is invalid. I tried adding the validator to onMount too, but nothing changes! ๐Ÿ˜ตโ€๐Ÿ’ซ
const form = useForm({
defaultValues: formData,
validators: {
onMount: createClientSchema, // zod schema
onChange: createClientSchema,
},
const form = useForm({
defaultValues: formData,
validators: {
onMount: createClientSchema, // zod schema
onChange: createClientSchema,
},
wise-white
wise-whiteโ€ข2mo ago
nothing changes? How are you using canSubmit? you might not be using it reactively also keep in mind that if a user attempts to submit on a pristine form, the onChange validator is still executed to ensure it's correct data so it's only the button you have to worry about, not invalid data reaching onSubmit
manual-pink
manual-pinkโ€ข2mo ago
I am using form.Subscribe thanks to you ๐Ÿ˜‰
import { Save, X } from "lucide-react";
import { DialogDescription } from "@radix-ui/react-dialog";
import type { ReactNode } from "react";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";

interface MaintenanceDialogProps {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
form: any;
open: boolean;
onOpenChange?: (open: boolean) => void;
title: string;
onCancel?: () => void;
children: ReactNode;
}

export default function MaintenanceDialog({
form,
open,
onOpenChange,
title,
onCancel,
children,
}: MaintenanceDialogProps) {
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent data-testid={testId}>
<DialogHeader>
<DialogTitle>
<DialogDescription>{title}</DialogDescription>
</DialogTitle>
</DialogHeader>
{children}
<DialogFooter>
<form.Subscribe
selector={(state) => [state.canSubmit, state.isSubmitting]}
children={([canSubmit, isSubmitting]) => (
<>
<Button data-testid="button-cancel" type="button" variant="outline" onClick={onCancel}>
<X />
Cancelar
</Button>
<Button
variant={"success"}
onClick={form.handleSubmit}
disabled={isSubmitting || !canSubmit}
>
<Save />
{isSubmitting ? "Guardando..." : "Guardar"}
</Button>
</>
)}
/>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
import { Save, X } from "lucide-react";
import { DialogDescription } from "@radix-ui/react-dialog";
import type { ReactNode } from "react";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";

interface MaintenanceDialogProps {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
form: any;
open: boolean;
onOpenChange?: (open: boolean) => void;
title: string;
onCancel?: () => void;
children: ReactNode;
}

export default function MaintenanceDialog({
form,
open,
onOpenChange,
title,
onCancel,
children,
}: MaintenanceDialogProps) {
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent data-testid={testId}>
<DialogHeader>
<DialogTitle>
<DialogDescription>{title}</DialogDescription>
</DialogTitle>
</DialogHeader>
{children}
<DialogFooter>
<form.Subscribe
selector={(state) => [state.canSubmit, state.isSubmitting]}
children={([canSubmit, isSubmitting]) => (
<>
<Button data-testid="button-cancel" type="button" variant="outline" onClick={onCancel}>
<X />
Cancelar
</Button>
<Button
variant={"success"}
onClick={form.handleSubmit}
disabled={isSubmitting || !canSubmit}
>
<Save />
{isSubmitting ? "Guardando..." : "Guardar"}
</Button>
</>
)}
/>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
Yes it is working fine, the form is not submitted and the validation error is shown, but the button is not initially disabled
wise-white
wise-whiteโ€ข2mo ago
it's not its default state, yeah. It will likely have one render cycle between that and onMount errors showing up However, considering * You want the initial state to be false * Changing the form runs the onChange validator, ensuring you can't continue submitting isPristine is the optimal way to go about it though I should look into why the form renders before mount errors appear sometime ...
manual-pink
manual-pinkโ€ข2mo ago
In my case, using isPristine is good enough, but what if the initial form values are valid? Then the save button will be disabled because isPristine is true
wise-white
wise-whiteโ€ข2mo ago
hmm ... I guess I should look into this sometime
manual-pink
manual-pinkโ€ข2mo ago
Thanks! I will wait for Leonardo's reply, I hope he will read this...
wise-white
wise-whiteโ€ข2mo ago
in the meantime, give form.state.isValid a try should be more strict than canSubmit
manual-pink
manual-pinkโ€ข2mo ago
I have just tried, isValid is initially true while the validation is invalid ๐Ÿ˜”
genetic-orange
genetic-orangeโ€ข3w ago
I'm also hitting this same issue. Was using canSubmit and now tried isValid but i have the same result where isValid is initially true even when the form is invalid
conscious-sapphire
conscious-sapphireโ€ข3w ago
Can you share some code? Here's a working example: https://stackblitz.com/edit/tanstack-form-fkwghscy?file=src%2Findex.tsx
Pascal Kรผsgen
StackBlitz
Form Simple Example (duplicated) - StackBlitz
Run official live example code for Form Simple, created by Tanstack on StackBlitz
genetic-orange
genetic-orangeโ€ข3w ago
Hey Ksgn thanks so much for that, ended up not being able to reproduce it (typical...) and led me to dig deeper into my implementation which had a bad useEffect hook causing form reset on initial render. Sorry mate, apprecitate the help! Think i found this thread which validated my thoughts it was a bug in the library and stopped looking at my own code... ๐Ÿคฆโ€โ™‚๏ธ @Jaime02 are you also doing something like this?
manual-pink
manual-pinkโ€ข3w ago
Yes exactly, I also have to take a look at this again carefully ๐Ÿ˜ตโ€๐Ÿ’ซ thanks for the research you all

Did you find this page helpful?