T
TanStack6mo ago
exotic-emerald

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
exotic-emerald
exotic-emeraldOP6mo 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>
);
plain-purple
plain-purple6mo 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
exotic-emerald
exotic-emeraldOP6mo 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.
plain-purple
plain-purple6mo 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...
plain-purple
plain-purple6mo ago
the current workaround until this is merged:
- form.reset()
+ setTimeout(() => form.reset(), 0)
- form.reset()
+ setTimeout(() => form.reset(), 0)
exotic-emerald
exotic-emeraldOP6mo ago
Aha! Thanks! Hhmm still results in a UI glitch, anyway will wait until that PR gets merged 👍 thanks for helping out!
plain-purple
plain-purple6mo 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
exotic-emerald
exotic-emeraldOP6mo ago
sure, happy to help! just tested it and it didn't resolve the UI glitch issue I added a comment to the PR 👍
plain-purple
plain-purple6mo 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
exotic-emerald
exotic-emeraldOP6mo 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>
);
}
plain-purple
plain-purple6mo 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
exotic-emerald
exotic-emeraldOP6mo 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: ...,
});
},
});
plain-purple
plain-purple6mo 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
exotic-emerald
exotic-emeraldOP6mo ago
great work guys! but normally i would expect that tanstack form is smart enough to reset the form when there are new values?
plain-purple
plain-purple6mo ago
it would overwrite what users have entered which is bad dx
exotic-emerald
exotic-emeraldOP6mo ago
similar to how RHF works when they introduced the values prop
plain-purple
plain-purple6mo ago
remember, React makes new objects on every render so technically you always give it new data
exotic-emerald
exotic-emeraldOP6mo ago
yeah indeed but how come RHF has that functionality OOTB?
plain-purple
plain-purple6mo 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
metropolitan-bronze
metropolitan-bronze6mo ago
Thanks for looking into this!

Did you find this page helpful?