T
TanStackβ€’2y ago
xenophobic-harlequin

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
wise-white
wise-whiteβ€’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
xenophobic-harlequin
xenophobic-harlequinOPβ€’2y ago
I'm using plain React. Not NextJS, unfortunately, but that's a good idea.
wise-white
wise-whiteβ€’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?
xenophobic-harlequin
xenophobic-harlequinOPβ€’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.
wise-white
wise-whiteβ€’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",
},
};
});
}, [])
xenophobic-harlequin
xenophobic-harlequinOPβ€’2y ago
I will try this Thanks
deep-jade
deep-jadeβ€’2y ago
This should be stable As in, it won't be likely to break in future releases for some time
xenophobic-harlequin
xenophobic-harlequinOPβ€’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.
deep-jade
deep-jadeβ€’2y ago
Huh. Let's open a bug and I'll investigate Minimal repro plz πŸ™‚
xenophobic-harlequin
xenophobic-harlequinOPβ€’2y ago
I will! Thanks Do you prefer codesandbox or stackblitz?
deep-jade
deep-jadeβ€’2y ago
I have a minor preference towards StackBlitz, but either way πŸ™‚
xenophobic-harlequin
xenophobic-harlequinOPβ€’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.
xenophobic-harlequin
xenophobic-harlequinOPβ€’2y ago
StackBlitz
React Starter - StackBlitz
React + TypeScript starter project
fair-rose
fair-roseβ€’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
fair-rose
fair-roseβ€’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?
fair-rose
fair-roseβ€’2y ago
Lol, if I put setXData(data) into the body of the fetchData function, the issue remains appears again.
fair-rose
fair-roseβ€’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
fair-rose
fair-roseβ€’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.
fair-rose
fair-roseβ€’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.
fair-rose
fair-roseβ€’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);
},
});
fair-rose
fair-roseβ€’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
deep-jade
deep-jadeβ€’2y ago
Can we capture this in a ticket? πŸ™‚
fair-rose
fair-roseβ€’2y ago
Sure, I'll make one later today! ☺️
fair-rose
fair-roseβ€’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?