Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updating defaultValue and value at different paths in the form? #1377

Open
rishitells opened this issue Apr 5, 2025 · 2 comments
Open

Updating defaultValue and value at different paths in the form? #1377

rishitells opened this issue Apr 5, 2025 · 2 comments

Comments

@rishitells
Copy link

rishitells commented Apr 5, 2025

Describe the bug

Note: I created this as a bug because I tried some api(s) to achieve what's mentioned below (like field.update()) but couldn't make it work and ran into errors. Please feel free to move it to discussions section if you think that's appropriate.

We are considering using TanStack form for our CMS content form as it really simplifies a lot of stuff for us, but we have an interesting challenge with TanStack Form that we previously faced with React Hook Form, hoping there might be a better solution. The issue comes up in our dynamic form where we have a list of items with nested data that loads on demand.

Here’s a simple example:

const form = useForm({
  defaultValues: {
    fruits: [
      { 
        id: 'A',
        name: 'apple',
        attributes: null  // loaded when expanded
      },
      { 
        id: 'B',
        name: 'banana',
        attributes: null
      }
    ]
  }
});

When a user expands a fruit item, we load its attributes. This works fine normally, but gets tricky when items are reordered before expanding. For example, if a user moves banana to the first position:

// Current form values after reordering:
fruits: [
  { id: 'B', name: 'banana', attributes: null },  // moved here
  { id: 'A', name: 'apple', attributes: null }
]

// But defaultValues are still:
defaultValues.fruits: [
  { id: 'A', ... },
  { id: 'B', ... }  // banana's defaults still here at index 1
]

Now when banana is expanded and we load its attributes, we need to:

  1. Set the defaultValue at fruits[1].attributes (where banana’s defaults live)
  2. Set the current value at fruits[0].attributes (where banana currently is)

In React Hook Form, we had to use resetField() which always updates both defaultValue and value at the same path. This forced us to use some hacks workarounds to maintain the correct state.

Is there a cleaner way in TanStack Form to:

  1. Update a field’s defaultValue at one path without affecting its current value? Or
  2. Update defaultValue and current value at different paths?

Your minimal, reproducible example

N/A

Steps to reproduce

We have a form with nested fields that load their data on demand. For example, when a parent item expands, we fetch and load its nested field data. This becomes tricky when the parent items have been reordered. When an item moves positions, its current value moves to the new position but its default value stays at the original position in the form state. So when we load the nested field data after expanding a moved item, we need to update its defaultValue at the original position but its current value at the new position. However, we can’t find a way in TanStack Form to update defaultValue and current value at different paths.

Expected behavior

Ability to update a field’s defaultValue and current value at different paths in the form state, or some way to update just the defaultValue of a field without affecting its current value.

How often does this bug happen?

None

Screenshots or Videos

No response

Platform

MacOS

TanStack Form adapter

react-form

TanStack Form version

1.3.0

TypeScript version

5.7.2

Additional context

No response

@LeCarbonator
Copy link
Contributor

If you dynamically add fields, I would go for a pattern like this:

{
  fruits: [{ id: 'A', name: 'Banana' }, { /* ... */ } ],
  ordering: [0, 1],
}

where ordering will swap values based on your ordering, but preserve the data structures it represents.

If you can guarantee that id will be unique, you can also go for a Record<string, Fruit> structure like this:

{
  fruits: {
    'A':  { name: 'Banana' }, 
    'B': { /* ... */ }
  },
  ordering: ['B', 'A'],
}

Would this help simplify your issue with defaultValues and their location?

@rishitells
Copy link
Author

@LeCarbonator Thanks for the suggestion!

However, we have a specific reason for needing in-place array updates. Our form state directly maps to our API operations - we generate GraphQL mutations by comparing the positions of items in the current values against their positions in default values. For example, if a user moves, updates, or deletes items, we generate instructions like:

{
  delete: [{ id: "3" }],
  update: [
    // Item 2 was moved to start and updated
    { where: { id: "2" }, data: { value: "updated" }, position: { start: true } },
    // Item 1 was moved after 2 and updated
    { where: { id: "1" }, data: { value: "updated" }, position: { after: "2" } },
  ],
  create: [
    // New items with specific positions
    { data: { value: "new" }, position: { after: "1" } }
  ]
}

This works very well for us and avoid complexities.

Using a separate ordering array would require us to either:

  1. Completely redesign our API transformation logic, or
  2. Add complex logic to reconstruct the actual array order before generating API operations

Additionally, we have a large existing codebase built around this array-based structure where positions directly represent user actions. A fundamental change like this would require a massive refactor effort that we can’t undertake right now.

That’s why we’re specifically looking for a way to handle defaultValue/current value updates within our current structure. Is there perhaps a way to do this without changing our form’s data structure?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants