T
TanStack•7d ago
deep-jade

Partial reset of a modified form

Hello! I have a question relative to form reset and defaultValues. Right now, I have a form that gets async initial values through TanStack Query, and it's working just fine. It even updates the defaultValues when the data changes, perfect. The important detail though, is that if the form is touched, the defaultValues will not be updated anymore. It makes total sense, you wouldn't want to override the changes made by the user. But would there be a way to detect which fields have been modified and which ones haven't, so we could still update the fields that the user didn't modify with the new data? The main scenario is for multi-user forms, where a user might update a bunch of fields, and we would like to propagate these changes to the other users, without losing their current changes.
7 Replies
continuing-cyan
continuing-cyan•7d ago
yes, the current update only checks the entire state of the form instead of partial checks. Personally, I think it may even be too eager, and the behaviour you describe would make more sense. It hasn't been brought up often before, so I'll read through chatlog to see if there was a conclusion for this. Could you elaborate on the main scenario you mentioned? A small example of what user A might update and how users B and C should receive the data
deep-jade
deep-jadeOP•7d ago
It can be any type of change really. As a small example, if you have a form with these default values from the DB.
{
firstName: 'John',
lastName: 'Doe',
address: {
street: '123 Main St',
city: 'Anytown',
state: 'CA',
},
children: [
{ firstName: 'Jane', lastName: 'Doe' },
{ firstName: 'Jim', lastName: 'Beam' },
],
}
{
firstName: 'John',
lastName: 'Doe',
address: {
street: '123 Main St',
city: 'Anytown',
state: 'CA',
},
children: [
{ firstName: 'Jane', lastName: 'Doe' },
{ firstName: 'Jim', lastName: 'Beam' },
],
}
Now say User A is changing the data to this but didn't save yet
{
firstName: 'Frank', // changed
lastName: 'Doe',
address: {
street: '123 Main St',
city: 'Newtown', // changed
state: 'TX', // changed
},
children: [
{ firstName: 'Jane', lastName: 'Doe' },
// Jim removed
],
}
{
firstName: 'Frank', // changed
lastName: 'Doe',
address: {
street: '123 Main St',
city: 'Newtown', // changed
state: 'TX', // changed
},
children: [
{ firstName: 'Jane', lastName: 'Doe' },
// Jim removed
],
}
In the meantime, user B already updated and saved the form with this new set of data:
{
firstName: 'Bob',
lastName: 'Smith',
address: {
street: '456 Other St',
city: 'Anytown',
state: 'CA',
},
children: [
{ firstName: 'Jane', lastName: 'Doe' },
{ firstName: 'Jim', lastName: 'Beam' },
{ firstName: 'Joe', lastName: 'Black' },
],
}
{
firstName: 'Bob',
lastName: 'Smith',
address: {
street: '456 Other St',
city: 'Anytown',
state: 'CA',
},
children: [
{ firstName: 'Jane', lastName: 'Doe' },
{ firstName: 'Jim', lastName: 'Beam' },
{ firstName: 'Joe', lastName: 'Black' },
],
}
The expectation (ideally) would be for user A to get their form automatically updated to this:
{
firstName: 'Frank', // local changes kept
lastName: 'Smith', // New value from DB
address: {
street: '456 Other St', // New value from DB
city: 'Newtown', // local change kept
state: 'TX', // local change kept
},
children: [
{ firstName: 'Jane', lastName: 'Doe' }, // Same value, no user changed it
// Jim is removed (local change kept)
{ firstName: 'Joe', lastName: 'Black' }, // New value from DB
],
}
{
firstName: 'Frank', // local changes kept
lastName: 'Smith', // New value from DB
address: {
street: '456 Other St', // New value from DB
city: 'Newtown', // local change kept
state: 'TX', // local change kept
},
children: [
{ firstName: 'Jane', lastName: 'Doe' }, // Same value, no user changed it
// Jim is removed (local change kept)
{ firstName: 'Joe', lastName: 'Black' }, // New value from DB
],
}
User C that didn't change anything would get all the new data from User B. I don't know if that helps like this? If we map through the fields and check each field state instead of the form state, that should be most of it covered. The trickier part would be for array fields I imagine
continuing-cyan
continuing-cyan•7d ago
I see ... consider this from a UX perspective though I see the client John Doe and I want to update his information. I open the form and start changing values. Suddenly, other values in the form start updating 'out of nowhere', because your webhook informed the frontend that there has been a change in the data. I lost control of values that I might have wanted to change, but are now a different value than before. User C on the other hand sees the client John Doe, and since he isn't modifying the values, he receives the values in real time. This assumes that the form is structured in this way, of course. There's multiple use cases where 2 or more users work on the same data, which you'd like to display as the most up to date value. What if user B overwrote values that A has also overwritten? User A decided that the first name is "Jane", but User B has changed all fields. Now user A only receives some of the updates that he should be aware of To use the example above:
address: {
street: '456 Other St', // New value from DB
city: 'Newtown', // local change kept
state: 'TX', // local change kept
},
address: {
street: '456 Other St', // New value from DB
city: 'Newtown', // local change kept
state: 'TX', // local change kept
},
The update caused this field to have become nonsense. There is no 456 Other St. in Newtown TX
deep-jade
deep-jadeOP•7d ago
Yes, it definitely brings some side effects like the ones you mention. There are ways to mitigate some of them, for example by notifying the user that there were changes and suggesting some actions to handle them properly, but these are extra steps that cannot be covered by the library, every case is different.
continuing-cyan
continuing-cyan•7d ago
Anyways, lots of talk, no discussions of what to do with this information yet. There is a way you can check for field changes, but it currently isn't doable by just passing in defaultValues. The way I would try to do this for v1.19 is to iterate through form.fieldInfo. It is a record of DeepKeys<FormData> and information about the field. One problem that might be tricky is how to resolve the data on your end for it
for (const [fieldName, fieldInfo] of Object.entries(form.fieldInfo)) {
// if the field hasn't mounted yet or is not dirty
if (!fieldInfo.instance?.state.meta.isDirty) {
form.setFieldValue(fieldName, newValue) // determine how to access newValue? I'm not sure
}
}
for (const [fieldName, fieldInfo] of Object.entries(form.fieldInfo)) {
// if the field hasn't mounted yet or is not dirty
if (!fieldInfo.instance?.state.meta.isDirty) {
form.setFieldValue(fieldName, newValue) // determine how to access newValue? I'm not sure
}
}
I'll bring up the partial async defaultValues to the other maintainers, see what they say. As I mentioned above, I'm not opposed to partial updates, but I may be missing some crucial detail on tanstack form's implementation that would break this.
deep-jade
deep-jadeOP•7d ago
Alright, no worries. Currently, the way we access new values now is with WebSockets, when a user saves the form it sends a message that triggers cache invalidation which in turn triggers a refetch, and the new data trigger the update function, which skips it if touched. That for...of is definitely something I can try for now, yes. I was just curious if there was an existing cleaner way to do it, or if it could be useful to make the update function more flexible for these advanced scenarios. Thanks a lot for your help and feedback on this 🙂
continuing-cyan
continuing-cyan•7d ago
there sort of is? I think the mergeForm function for SSR libraries goes in this direction. However, it's for a different use case and won't have the correct type for this but that's just the implementation details. It will likely be an option to enable that keeps checking for defaultValues updates

Did you find this page helpful?