0

I have the following data generated by an API:

{
  "config": {
    1: {"slot1": "SK_DISENGAGE"},
    2: {"slot1": "SK_DISENGAGE"}
  }
}

I transformed config into this array of objects below, as it's easier to deal with when displaying it via react-hook-form, and because the items in the object are subject to change:

{
  "config": [
    {"slot1": "SK_DISENGAGE", element: 1},
    {"slot1": "SK_DISENGAGE", element: 2}
  ]
}

Here's a codesandbox of the code below:

function App() {
  const { register, control, handleSubmit, reset, watch } = useForm({
    defaultValues: {
      config: [
        { slot1: "SK_DISENGAGE", element: 1 },
        { slot1: "SK_DISENGAGE", element: 2 }
      ]
    }
  });
  // Simplified. The data comes from an API, and useForm would be empty until after the async functions are done.

  const { fields } = useFieldArray({ control, name: "config" });

  const onSubmit = (data) => console.log("data", data);

  renderCount++;

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <h1>Field Array </h1>
        {fields.map((item, index) => {
          return (
            <li key={item.id}>
              {`Item:`}
              <input
                name={`config.${item.element}.slot1`}
                defaultValue={`${item.slot1}`} 
                ref={register()}
              />
            </li>
          );
        })}
      </ul>
      <input type="submit" />
    </form>
  );
}

However, I'm having trouble transforming it back, and it ends up like this:

{
  "config": [
    null,
    { "slot1": "SK_DISENGAGE" },
    { "slot1": "SK_DISENGAGE" }
  ]
}

config is now an array of objects, a format the API will not take kindly to. I've tried modifying the name field in the input tag, adding ", ', or no quotes to item.element, but the results are the same.

I assume the first element is null because nothing is setting the element 0.

How do I programmatically set a number as an object key? Is there a slash or something I should put in so it should be taken as a string?

zack_falcon
  • 4,186
  • 20
  • 62
  • 108

3 Answers3

1

You can use Array.prototype.reduce() method to achieve your desired output. The syntax of array reduce array.reduce(function(total, currentValue, currentIndex, arr), initialValue).

const data = {
  config: [
    { slot1: 'SK_DISENGAGE', element: 1 },
    { slot1: 'SK_DISENGAGE', element: 2 },
  ],
};

const ret = {
  config: data.config.reduce((prev, c) => {
    const p = prev;
    p[c.element] = { slot1: c.slot1 };
    return p;
  }, {}),
};
console.log(ret);
mr hr
  • 3,162
  • 2
  • 9
  • 19
1

If you change your field mapping to this:

{fields.map((item, index) => {
      return (
        <li key={item.id}>
          {`Item:`}
          <input
            name={item.element}
            defaultValue={`${item.slot1}`} // make sure to set up defaultValue
            ref={register()}
          />
        </li>
      );
    })}

And your onSubmit to this:

const onSubmit = (data) => {

    var newData = Object.entries(data).reduce( (acc, [key,value]) => {
      acc[key] = {slot1:value};
      return acc;
    },{});
    console.log("data", newData);
}

The result is pretty close to your original input.... does that work for you?

Fork of your code: https://codesandbox.io/s/react-hook-form-usefieldarray-forked-olcn3

Jamiec
  • 133,658
  • 13
  • 134
  • 193
0

Append an array field to the original object

{
  "config": {
    1: {"slot1": "SK_DISENGAGE"},
    2: {"slot1": "SK_DISENGAGE"}
  },
  "config-arr": [
    {"slot1": "SK_DISENGAGE", element: 1},
    {"slot1": "SK_DISENGAGE", element: 2}
  ]
}
Abishek Kumar
  • 519
  • 5
  • 13
  • Unfortunately, that's not the result I want, although this appears to be born out of misunderstanding due to the way I worded my question. I've added a code sandbox to my question. Please have a look if you have the time. Thanks. – zack_falcon Nov 06 '20 at 14:03