T
TanStack3mo ago
passive-yellow

isDefaultValue remains false after successful form submission

Hey guys, after successfully submitting a form and invalidating the data, the newly introduced isDefaultValue remains false, while it should become true. Has anyone experienced this issue?
20 Replies
passive-yellow
passive-yellowOP3mo ago
Some example code:
const form = useForm({
defaultValues: {
name: props.name,
},
onSubmit: async ({ value }) => {
// call mutation, which invalidates a query, after query invalidation, props.name is updated but `isDefaultValue` remains false, while i would think it should reset, no?
},
});

return (
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
<form.Field name="name">
{(field) => (
<FormItem>
<FormLabel htmlFor={field.name}>Name</FormLabel>
<FormControl>
<Input
id={field.name}
name={field.name}
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
/>
</FormControl>
</FormItem>
)}
</form.Field>
<form.Subscribe selector={(state) => [state.isSubmitting, state.isDefaultValue]}>
{([isSubmitting, isFormUnchanged]) => (
<Button
type="submit"
disabled={isSubmitting || isFormUnchanged}
onClick={form.handleSubmit}
>
{isSubmitting ? 'Saving...' : 'Save'}
</Button>
)}
</form.Subscribe>
</form>
);
const form = useForm({
defaultValues: {
name: props.name,
},
onSubmit: async ({ value }) => {
// call mutation, which invalidates a query, after query invalidation, props.name is updated but `isDefaultValue` remains false, while i would think it should reset, no?
},
});

return (
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
<form.Field name="name">
{(field) => (
<FormItem>
<FormLabel htmlFor={field.name}>Name</FormLabel>
<FormControl>
<Input
id={field.name}
name={field.name}
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
/>
</FormControl>
</FormItem>
)}
</form.Field>
<form.Subscribe selector={(state) => [state.isSubmitting, state.isDefaultValue]}>
{([isSubmitting, isFormUnchanged]) => (
<Button
type="submit"
disabled={isSubmitting || isFormUnchanged}
onClick={form.handleSubmit}
>
{isSubmitting ? 'Saving...' : 'Save'}
</Button>
)}
</form.Subscribe>
</form>
);
deep-jade
deep-jade3mo ago
could be a bug. Feel free to open up an issue with the code snippet on stackblitz oh, actually just noticed something. The form isn't reset in your code snippet, so that's probably why it remains as for async defaultValues, it would overwrite the user's input, so if the form is touched it no longer overwrites it
passive-yellow
passive-yellowOP3mo ago
So I have to reset the form like this: form.reset({ name: props.name })? That works, but creates a weird UI glitch where you see the previous value for a split second. Not a big fan of this approach, I remember React Hook Form having a similar issue, so they introduces "async" values next to the defaultValues they already provided.
deep-jade
deep-jade3mo ago
GitHub
fix(react): form.reset not working inside of onSubmit by harry-whor...
Relates to #1490, #1485 and potentially #1487. This appears to stem from the react adapter as Angular, Vue and Core all seem to work as intended. Reset will work correctly initially then on next pa...
deep-jade
deep-jade3mo ago
the current workaround until this is merged:
- form.reset()
+ setTimeout(() => form.reset(), 0)
- form.reset()
+ setTimeout(() => form.reset(), 0)
passive-yellow
passive-yellowOP3mo ago
Aha! Thanks! Hhmm still results in a UI glitch, anyway will wait until that PR gets merged 👍 thanks for helping out!
deep-jade
deep-jade3mo ago
you can test the PR version real quick if you want. It would be great feedback for the PR author
npm i https://pkg.pr.new/@tanstack/react-form@1494
npm i https://pkg.pr.new/@tanstack/react-form@1494
see if it fixes the UI glitch
passive-yellow
passive-yellowOP3mo ago
sure, happy to help! just tested it and it didn't resolve the UI glitch issue I added a comment to the PR 👍
deep-jade
deep-jade3mo ago
what‘s the snippet you tried? I‘ve given the PR a test on some of my code and it seems to work fine
passive-yellow
passive-yellowOP3mo ago
Here is a simplified version of my form:
export function TestComponent(props: {
data: {
name: string;
};
}) {
const form = useForm({
defaultValues: {
name: props.data?.name ?? '',
},
onSubmit: async ({ value }) => {
// does mutation and invalidates the query (data prop that is passed in)
// on mutation success it also calls reset(props.data)
},
});

return (
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
void form.handleSubmit();
}}
>
<form.Field name="name">
{(field) => (
<FormItem>
<FormLabel htmlFor={field.name}>Name</FormLabel>
<FormControl>
<Input
id={field.name}
name={field.name}
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
/>
</FormControl>
</FormItem>
)}
</form.Field>
<Button type="submit">Submit</Button>
</form>
);
}
export function TestComponent(props: {
data: {
name: string;
};
}) {
const form = useForm({
defaultValues: {
name: props.data?.name ?? '',
},
onSubmit: async ({ value }) => {
// does mutation and invalidates the query (data prop that is passed in)
// on mutation success it also calls reset(props.data)
},
});

return (
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
void form.handleSubmit();
}}
>
<form.Field name="name">
{(field) => (
<FormItem>
<FormLabel htmlFor={field.name}>Name</FormLabel>
<FormControl>
<Input
id={field.name}
name={field.name}
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
/>
</FormControl>
</FormItem>
)}
</form.Field>
<Button type="submit">Submit</Button>
</form>
);
}
deep-jade
deep-jade3mo ago
I mean the code snippet in onSubmit specifically. Doesn‘t need to be real names or anything, but maybe there‘s something with the order of operations
passive-yellow
passive-yellowOP3mo ago
NVM 😅 for some reason your request triggered a solution in my head :p
const mutation = useMutation({
mutationFn,
onSuccess: async () => {
// the await was the missing piece, without it, the reset will happen too soon which caused the UI glitch as it was using the old values for a split second but then the component re-rendered
await queryClient.invalidateQueries({ ... });
form.reset();
},
});

const form = useForm({
defaultValues: {
name: props.data.name
},
onSubmit: async ({ value }) => {
await mutation.mutateAsync({
data: ...,
});
},
});
const mutation = useMutation({
mutationFn,
onSuccess: async () => {
// the await was the missing piece, without it, the reset will happen too soon which caused the UI glitch as it was using the old values for a split second but then the component re-rendered
await queryClient.invalidateQueries({ ... });
form.reset();
},
});

const form = useForm({
defaultValues: {
name: props.data.name
},
onSubmit: async ({ value }) => {
await mutation.mutateAsync({
data: ...,
});
},
});
deep-jade
deep-jade3mo ago
glad to hear it worked! I figured it may be that, but this PR has been such a headache that it could still have been an edge case
passive-yellow
passive-yellowOP3mo ago
great work guys! but normally i would expect that tanstack form is smart enough to reset the form when there are new values?
deep-jade
deep-jade3mo ago
it would overwrite what users have entered which is bad dx
passive-yellow
passive-yellowOP3mo ago
similar to how RHF works when they introduced the values prop
deep-jade
deep-jade3mo ago
remember, React makes new objects on every render so technically you always give it new data
passive-yellow
passive-yellowOP3mo ago
yeah indeed but how come RHF has that functionality OOTB?
deep-jade
deep-jade3mo ago
I‘m not sure! I‘ll look into how they handle it anyways, false alarm @nneaowwplane , the PR does seem to fix the UI glitch
rival-black
rival-black3mo ago
Thanks for looking into this!

Did you find this page helpful?