T
TanStack•7mo ago
rival-black

form defaultValues doesn't clear with initial data retrieved

hey all! posting my first question here in a confusing situation i'm in. I use tanstack query to fetch data from an endpoint, and tanstackform to handle em. I've a two dropdown screen, in which the second dropdown is clearable/deletable by users but i'm unable to reach that state which falls back to form.defaultValues instead of users able to remove its node completely. What happens is that if I commented this code out if (!country) return; it deletes the value from the dropdown, but i'm left with my object like this: [{"country": "AD","verId": "1"},{"country": "","verId": ""}] and if I didn't, i'm unable to remove anything, anytimg i try to click on the delete button, it doesn't do anything and i'm left with the default values.
const form = useForm({defaultValues: {taxResidencies: userData?.taxResidencies ?? ([] as TaxResidency[]),},onSubmit: async ({ value }) => {}});
const form = useForm({defaultValues: {taxResidencies: userData?.taxResidencies ?? ([] as TaxResidency[]),},onSubmit: async ({ value }) => {}});
16 Replies
rival-black
rival-blackOP•7mo ago
and my form field is: <form.Field name="taxResidencies[1].country" validators={{ onChangeListenTo: ["taxResidencies[0].country"], onChange: ({ value }) => { if (!value) { return undefined; } return undefined; }, }} > {(field) => ( <> <form.Subscribe selector={(state) => state.values.taxResidencies[0]?.country}> {(primaryCountry) => ( <Dropdown ......... onChange={(value: string | undefined) => { if (value === undefined) { const primaryResidency = { country: form.getFieldValue("taxResidencies[0].country"), taxId: form.getFieldValue("taxResidencies[0].verId"), } as TaxResidency; form.resetField("taxResidencies[1].country"); form.resetField("taxResidencies[1].verId"); form.setFieldValue("taxResidencies", [primaryResidency]); } const country = value as LegalCountries; // type of country codes e.g. "SE", "IT" ... // if (!country) return; const taxId = country === "ES" ? null : ""; field.form.setFieldValue("taxResidencies[1].country", country); field.form.setFieldValue("taxResidencies[1].verId", verId); }} clearable={true} /> )} </form.Subscribe> </> )} </form.Field>
xenial-black
xenial-black•7mo ago
Formatted for Discord:
<form.Field
name="taxResidencies[1].country"
validators={{
onChangeListenTo: ["taxResidencies[0].country"],
onChange: ({ value }) => {
if (!value) {
return undefined;
}
return undefined;
},
}}
>
{(field) => (
<>
<form.Subscribe selector={(state) => state.values.taxResidencies[0]?.country}>
{(primaryCountry) => (
<Dropdown
.........
onChange={(value: string | undefined) => {
if (value === undefined) {
const primaryResidency = {
country: form.getFieldValue("taxResidencies[0].country"),
taxId: form.getFieldValue("taxResidencies[0].verId"),
} as TaxResidency;

form.resetField("taxResidencies[1].country");
form.resetField("taxResidencies[1].verId");
form.setFieldValue("taxResidencies", [primaryResidency]);
}
const country = value as LegalCountries; // type of country codes e.g. "SE", "IT" ...
// if (!country) return;
const taxId = country === "ES" ? null : "";
field.form.setFieldValue("taxResidencies[1].country", country);
field.form.setFieldValue("taxResidencies[1].verId", verId);
}}
clearable={true}
/>
)}
</form.Subscribe>
</>
)}
</form.Field>
<form.Field
name="taxResidencies[1].country"
validators={{
onChangeListenTo: ["taxResidencies[0].country"],
onChange: ({ value }) => {
if (!value) {
return undefined;
}
return undefined;
},
}}
>
{(field) => (
<>
<form.Subscribe selector={(state) => state.values.taxResidencies[0]?.country}>
{(primaryCountry) => (
<Dropdown
.........
onChange={(value: string | undefined) => {
if (value === undefined) {
const primaryResidency = {
country: form.getFieldValue("taxResidencies[0].country"),
taxId: form.getFieldValue("taxResidencies[0].verId"),
} as TaxResidency;

form.resetField("taxResidencies[1].country");
form.resetField("taxResidencies[1].verId");
form.setFieldValue("taxResidencies", [primaryResidency]);
}
const country = value as LegalCountries; // type of country codes e.g. "SE", "IT" ...
// if (!country) return;
const taxId = country === "ES" ? null : "";
field.form.setFieldValue("taxResidencies[1].country", country);
field.form.setFieldValue("taxResidencies[1].verId", verId);
}}
clearable={true}
/>
)}
</form.Subscribe>
</>
)}
</form.Field>
So you have a structure of two dropdowns with one being completely removable?
rival-black
rival-blackOP•7mo ago
Exactly, and thanks for the reformatting 🙂 the second dropdown is dependant on the first one, but that's not there issue so far. if I removed the default values, all of this works as expected. But after fetching the data from the enpoint and assigning it's response to the form's defaultValues, things are breaking ..
xenial-black
xenial-black•7mo ago
do you have an example of what userData could look like?
rival-black
rival-blackOP•7mo ago
userData has many properties, but for the defaultValues at least userData.taxResidencies is:
(property) taxResidencies: ({
country: "ES";
verId: null;
} | {
country: "AD" | "BE" | "DK" | "EE" | "FI" | "FR" | "GR" | "GB" | "GP" | "IE" | "IS" | "IT" | "JP" | "CA" | "LV" | "LT" | "LU" | "MQ" | "MC" | "NZ" | "NL" | ... 13 more ... | "HU";
verId: string;
})[] | null | undefined
(property) taxResidencies: ({
country: "ES";
verId: null;
} | {
country: "AD" | "BE" | "DK" | "EE" | "FI" | "FR" | "GR" | "GB" | "GP" | "IE" | "IS" | "IT" | "JP" | "CA" | "LV" | "LT" | "LU" | "MQ" | "MC" | "NZ" | "NL" | ... 13 more ... | "HU";
verId: string;
})[] | null | undefined
it's a descriminated union that requires verId only for non spanish values.
xenial-black
xenial-black•7mo ago
I will use that and the provided field to create a stackblitz reproduction, if that's alright that way, it's easier for me (or others seeing this post) to edit the values and test
rival-black
rival-blackOP•7mo ago
@Luca | LeCarbonator i tried to mock as much as I can, here we go with the issue reproduced: https://stackblitz.com/edit/vitejs-vite-ktgdr9dv?file=src%2FApp.tsx thanks in advance 🙂
mh4md.a@gmail.com
StackBlitz
Vitejs - Vite (forked) - StackBlitz
Next generation frontend tooling. It&#39;s fast!
xenial-black
xenial-black•7mo ago
great! I'll take a look later Some notes: - You are calling reset on the field that you proceed to completely delete. There are helper methods like FormApi.deleteField for that instead - Your tax residencies are dependent on where they are in the array. It would probably simplify the code if you have a primaryTaxResidency and an array of additional ones (that may be empty) - instead of field.setValue(newValue), use field.handleChange(newValue) as the latter will handle change listeners and validators for you
rival-black
rival-blackOP•7mo ago
Thanks! I've changed to use deleteField instead of reset, and created two separate arrays to separate concerns of each field, used handlchange in favour of setValue in teh whole screen. Tho, I am still unable to reset back to set the additional country to undefined. It always go back to the defaultValues provided by the endpoint, while that I understand that this might be how tanstack-form works, but is there a way to still keep the defaultValues but override a node on a trigger? none of the below functions worked so far 😦
const resetAdditionalResidency = () => {
// form.setFieldValue("additionalTaxResidency", undefined); // this is working

// form.deleteField("additionalTaxResidency");
// form.setFieldValue("additionalTaxResidency", undefined);
// form.setFieldMeta("additionalTaxResidency", (meta) => ({
// ...meta,
// isDirty: true,
// }));

form.resetField("additionalTaxResidency");
console.log("additionalTaxResidency", form.getFieldValue("additionalTaxResidency"));
};
const resetAdditionalResidency = () => {
// form.setFieldValue("additionalTaxResidency", undefined); // this is working

// form.deleteField("additionalTaxResidency");
// form.setFieldValue("additionalTaxResidency", undefined);
// form.setFieldMeta("additionalTaxResidency", (meta) => ({
// ...meta,
// isDirty: true,
// }));

form.resetField("additionalTaxResidency");
console.log("additionalTaxResidency", form.getFieldValue("additionalTaxResidency"));
};
xenial-black
xenial-black•7mo ago
resetField is exactly for that, to reset the field back to the default value that was set. If you don't like the default value provided by the endpoint, then it's not really a default value and should be set instead overriding nodes can be done with * deleteField * setFieldValue * conditionally rendering it in React
rival-black
rival-blackOP•7mo ago
I mean i'm currently using form.deleteField("additionalTaxResidency"); but that doesn't really override the node. May I ask for more clarity about "conditionally rendering it in React"? 🙂 neither form.setFieldValue nor deleteField works so far to override a value unfortunately
xenial-black
xenial-black•7mo ago
Instead of resetting / setting the additional tax residency every time the primary changes, you can also prevent the second field from being visible based on a condition. Then in your onSubmit, you ignore the value based on that condition as well
rival-black
rival-blackOP•7mo ago
form.setFieldValue("additionalTaxResidency.country", "" as LegalCountryCode);
form.setFieldValue("additionalTaxResidency.taxId", "" as string);
form.setFieldValue("additionalTaxResidency.country", "" as LegalCountryCode);
form.setFieldValue("additionalTaxResidency.taxId", "" as string);
this does, I think i just need to sanitize it myself :), i'm still curious about hearing out your opinion tho 🙂
xenial-black
xenial-black•7mo ago
showSecondResidency && <YourComponent/>
rival-black
rival-blackOP•7mo ago
I really appreciate your input Luca, thanks! I think I have enough to try it out myself from here 🙇
xenial-black
xenial-black•7mo ago
:PepeThumbs:

Did you find this page helpful?