简体   繁体   中英

React Hook Form value is not changing when swapping or Deleting Groups

I'm making a nested form builder that is dynamic.

This is the current project I'm working on: https://codesandbox.io/s/ava-dynamic-react-hook-form-ivgt40?file=/src/App.js

The issue I'm having is that when I swap or delete Groups, the values within the Group aren't properly set.

I believe it is due to the {...register()} and the altering ref on my Input Components, but I am not certain.

The form data.js file in the linked project at the top contains the form structure I wish to render.

list: {
    id,
    label,
    control: "list" // identifier that this element is a list
    creatable: boolean,
    items: [
        [elements], // group 1
        [elements], // group 2
    ]
}
input: {
    id,
    label,
    control: "input" // identifier that this element is a input
    type: "text" // type of input
    defaultValue,
    rules: {
        required: {
            value: boolean,
            message: string,
        }
    }
}

State management

You seem to be mixing places to that are keeping tracking of state.

You basicly have 2 types of state

  • State of the form values | this is stored in the react-hook-form
  • State of the sort order of form inputs | this is stored in your Element.jsx

By mixing these, you have to manually make sure they stay in sync

react-hook-form

When you're using react-hook-form . you're entrusting some state management to that hook. They provide an API through register to keep track of the fields and values for the form.

Register takes a name as the first param.
This is the only handle you have for the form to identify fields.
A new name would be a new fields.

the problem

You're not keeping things in sync.
Changing the order will do a couple of things:

  • The current elementIdPath is something like Emailprofile.0.notifications.0.email.0.email-abcd123
  • Emailprofile.0.notifications.0.email.0.email-abcd123 is passed as name to the register.
  • click on the change order button
  • handleSwapListElements runs and changes the element via setElementItem
  • the elementIdPath changes: Emailprofile.0.notifications.0.email.1.email-abcd123 (notice index change from 0 to 1)
  • New name is registerd and treated as a new field
  • the old field is also still registered and in state. clicking submit will also have the old name and value.

The fix

Keep the sort order and values in sync. You'll have to decide yourself how exactly to do that. either:

  • don't include sort order or indexes as key in the name.
  • manually keep things in sync using react-hook-form methods.

Both have their ups and downs.

not including indexes as key

A dynamic form without indexes would require you to attach all that information on the field itself. Basically you would register a unique key (uuid), and keep track of the uuid path yourself.
IE you register to properties.:

 {
   "abcd123-value" : "some@email.de",
   "abcd123-path"  : "Emailprofile.0.notifications.0.email.0.email-abcd123" // this would not be shown to the user, but still exposed to the form, in the submit you would manually combine the information again. 
 }

manually keep things in sync using react-hook-form methods.

Whenever you change the name of a field, you would get the current value. unregister the old name and set the value on the new name.
react-hook-form exposes a method from the hook for that.

unregister and setValue

If it where up to me, and seeing your code.I would probably try to go in this direction as opposed to refactoring to things to remove the indexes from name.

sandbox

this sandbox with modifications should paint the picture. It will not properly work yet for the base elements, but the nodes at profile.0.notification.0.email should function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM