T
TanStackβ€’2y ago
dependent-tan

What's the recommended way to patch the entire form?

For example, if I'm using the same component to create/edit an entity, I want to replace the default values when there is data in the server for that entity. How can I push the server data into the form? Thanks.
24 Replies
adverse-sapphire
adverse-sapphireβ€’2y ago
So are you using the useForm hook? If so you could have a server component where you would fetch and see if there are values and in your client component you could take those values as prop and set it up in default values
dependent-tan
dependent-tanOPβ€’2y ago
I'm using plain React. Not NextJS, unfortunately, but that's a good idea.
adverse-sapphire
adverse-sapphireβ€’2y ago
you could also set the value like this
const {
state: stateDemo,
store,
update,
reset,
setFieldValue,
} = useForm({
defaultValues: {
name: "first value",
},
});

useEffect(() => {
console.log("stateDemo", stateDemo);
const newValue = "test";
setFieldValue("name", newValue);
}, []);
const {
state: stateDemo,
store,
update,
reset,
setFieldValue,
} = useForm({
defaultValues: {
name: "first value",
},
});

useEffect(() => {
console.log("stateDemo", stateDemo);
const newValue = "test";
setFieldValue("name", newValue);
}, []);
@crutchcorn would this be the correct approach?
dependent-tan
dependent-tanOPβ€’2y ago
That's one way. It did cause some issues when I was moving the form field into a child component but it is one way. I was wondering if it was a way to patch the entire form instead of having to enter field by field.
adverse-sapphire
adverse-sapphireβ€’2y ago
yes, you could also change the entire form like this
const { state: stateDemo, store } = useForm({
defaultValues: {
name: "first value",
another: "another value",
},
});

useEffect(() => {
console.log("stateDemo", stateDemo);
store.setState((state) => {
return {
...state,
values: {
name: "hello world",
another: "another demo value",
},
};
});
}, [])
const { state: stateDemo, store } = useForm({
defaultValues: {
name: "first value",
another: "another value",
},
});

useEffect(() => {
console.log("stateDemo", stateDemo);
store.setState((state) => {
return {
...state,
values: {
name: "hello world",
another: "another demo value",
},
};
});
}, [])
dependent-tan
dependent-tanOPβ€’2y ago
I will try this Thanks
fair-rose
fair-roseβ€’2y ago
This should be stable As in, it won't be likely to break in future releases for some time
dependent-tan
dependent-tanOPβ€’2y ago
@crutchcorn this doesn't seems to work for me. The form get the value, but the field does not reflect it. The same happened to me with the setFieldValue. It does happen that my field is in a child componet if that may be important.
fair-rose
fair-roseβ€’2y ago
Huh. Let's open a bug and I'll investigate Minimal repro plz πŸ™‚
dependent-tan
dependent-tanOPβ€’2y ago
I will! Thanks Do you prefer codesandbox or stackblitz?
fair-rose
fair-roseβ€’2y ago
I have a minor preference towards StackBlitz, but either way πŸ™‚
dependent-tan
dependent-tanOPβ€’2y ago
Got it! Uhm the problem is when the form is inside a conditional rendering like an if. I will create the issue soon.
dependent-tan
dependent-tanOPβ€’2y ago
StackBlitz
React Starter - StackBlitz
React + TypeScript starter project
other-emerald
other-emeraldβ€’2y ago
The field's value set in the useEffect hook appears and suddenly disappears if we force the input element to rerender when field.state.value changes. Interesting
other-emerald
other-emeraldβ€’2y ago
Ok, I think I made it work: πŸ”— https://stackblitz.com/edit/stackblitz-starters-gdmagd?file=src%2FApp.tsx I simply replaced the useEffect hook in your example with these lines:
const fetchData = useCallback(async () => {
const p1 = new Promise<{ firstName: string }>((res) =>
setTimeout(() => res({ firstName: 'Isabelle' }), 3000)
);
const data = await p1;

form.setFieldValue('firstName', data.firstName);
}, [form]);

useEffect(() => {
fetchData();
}, [fetchData]);
const fetchData = useCallback(async () => {
const p1 = new Promise<{ firstName: string }>((res) =>
setTimeout(() => res({ firstName: 'Isabelle' }), 3000)
);
const data = await p1;

form.setFieldValue('firstName', data.firstName);
}, [form]);

useEffect(() => {
fetchData();
}, [fetchData]);
Wdyt?
other-emerald
other-emeraldβ€’2y ago
Lol, if I put setXData(data) into the body of the fetchData function, the issue remains appears again.
other-emerald
other-emeraldβ€’2y ago
this kinda suggests to me that the issue is changing the state, maybe because that forces us to re-render the component, in which we run the (field) => <input .../> function this is just intuition at this point
other-emerald
other-emeraldβ€’2y ago
Let's test this hypothesis by profiling the two versions (modifying the state during data fetching vs not modifying): 1. Not modifying the state of <App /> during data fetching (first image) 1 rerender. Field changes bc one of its hooks. This must be because we changed its value when we called form.setFieldValue('firstName', data.firstName). 2. Modifying the state of <App /> during data fetching (second image) 3 commits. *Commit 1: * (2nd image): The Field rerenders, bc one of its hooks has changed, I think this is when we update it through the api. I think this is the point when see the value we set in the Field. Commit 2: (3rd and 4th image) <App /> rerenders, bc one of its hooks have changed. I think this is the setXData hook. This causes <Field/> to rerender too (4th image). This must be the point when our value disappears from the field and it starts with a blank state again. Commit 3: (5th image) Nothing important happens. We can ignore it.
other-emerald
other-emeraldβ€’2y ago
maybe if we change the default values of the form, it'll not instantiate the <Field /> with an empty value when it rerenders on the state change.
other-emerald
other-emeraldβ€’2y ago
Yes, indeed. This is so much fun 🀣
const [xData, setxData] = useState(null);

const form = useForm<{ firstName: string }>({
defaultValues: {
firstName: xData?.firstName,
},
onSubmit: async ({ value }) => {
console.log(value);
},
});
const [xData, setxData] = useState(null);

const form = useForm<{ firstName: string }>({
defaultValues: {
firstName: xData?.firstName,
},
onSubmit: async ({ value }) => {
console.log(value);
},
});
other-emerald
other-emeraldβ€’2y ago
Well, I'm not familiar with the Form internals and have a PR in tanstack.com to finish, so I can't continue with this now, but maybe the info I found helps someone else to pick this up
fair-rose
fair-roseβ€’2y ago
Can we capture this in a ticket? πŸ™‚
other-emerald
other-emeraldβ€’2y ago
Sure, I'll make one later today! ☺️
other-emerald
other-emeraldβ€’2y ago
Found an issue about this, so I just added comments to it: https://github.com/TanStack/form/issues/567#issuecomment-1890773421
GitHub
Programatic updates don't get shown on fields Β· Issue #567 Β· TanSta...
Describe the bug When updating the form value using either form.setFieldValue or form.store.setState((state) => { return { ...state, values: { ...data, }, }; }); The state gets lost, and it is n...

Did you find this page helpful?