reactjsreact-hooksreact-formsreact-forwardref

How to update the form data array of array in react-form-hook?


Last few days, I was trouble updating form data in an array of arrays in react-form-hook. I have attached the screenshot of my structure of fields, type and etc. I want to update the fields of fields data when I save the data.

I want to perform it with nested array with useFieldArray hooks which came from react-hook-form library.

I'm not getting any error on it but data was not passed correctly in the database. How can I fix it?

export const defects = {
  title: 'Defects',
  fields: [
    {
      title: 'Defect Location:',
      name: 'defects.location',
      type: 'checkbox',
      fields: [
        {
          title: 'Interior',
          type: 'checkbox',
          name: 'defects.location.interior',
        },
        {
          title: 'Roof space',
          type: 'checkbox',
          name: 'defects.location.roofSpace',
        },
        {
          title: 'Other',
          type: 'textarea',
          name: 'defects.location.other',
        },
      ],
    },
    {
      title: 'Defect Summary:',
      name: 'defects.summary',
      type: 'checkbox',
      fields: [
        {
          title: 'Roof Framing',
          type: 'checkbox',
          name: 'defects.summary.roofFraming',
        },
        {
          title: 'Porch Support',
          type: 'checkbox',
          name: 'defects.summary.porchSupport',
        },
        {
          title: 'Alfresco Support',
          type: 'checkbox',
          name: 'defects.summary.alfrescoSupport',
        },
        {
          title: 'Gate Feature',
          type: 'checkbox',
          name: 'defects.summary.gateFeature',
        },
        {
          title: 'Subfloor Structure',
          type: 'checkbox',
          name: 'defects.summary.subfloorStructure',
        },
        {
          title: 'Brick',
          type: 'checkbox',
          name: 'defects.summary.brick',
        },
        {
          title: 'Other',
          type: 'textarea',
        },
      ],
    },
  ],
}

Parent Component

  const { register, handleSubmit, errors, setValue, getValues, control, watch, reset } =
    useForm({ shouldUnregister: false })

  const { fields, append, remove, update } = useFieldArray({
    control: control,
    name: defects.title,
  })

  const onSubmit = async (data) => {
    const values = getValues()
    console.log('data is here', data, values)
    const response = await fetch('/api/db/handler', {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    })
    if (response.ok) {
      setSaved(true)
      setTimeout(() => {
        setSaved(false)
      }, 2000)
    }
    return response
  }

return(
         <Defects
                data={defects}
                register={register}
                setValue={setValue}
                control={control}
                watch={watch}
                fields={fields}
                append={append}
                remove={remove}
                update={update}
              />
    )

Defect JS

const Defects = ({
  register,
  setValue,
  control,
  watch,
  data,
  fields,
  append,
  remove,
  update,
}) => {
  useEffect(() => {
    update();
  }, [register]);

  return (
    <div>
     
      {fields.map((props, index) => {
        return (
          <Disclosure
            defaultOpen
            open={true}
            as='div'
            key={props.id}
            className='border-b-4 p-4 border-gray-100'
          >
            {index > 0 ? (
              <div className='flex items-center'>
                <Disclosure.Button className='text-xl uppercase underline flex items-center'>
                  {`DEFEECT ${index}`}
                  <FaChevronDown className='ml-4 ui-open:rotate-180 ui-open:transform' />
                </Disclosure.Button>
                <button
                  className='bg-sky-400 text-white px-4 py-2 rounded mt-4 ml-12'
                  onClick={() => remove(index)}
                >
                  Delete
                </button>
              </div>
            ) : (
              <div className='flex items-center'>
                <Disclosure.Button className='text-xl uppercase underline flex items-center'>
                  DEFEECTS
                  <FaChevronDown className='ml-4 ui-open:rotate-180 ui-open:transform' />
                </Disclosure.Button>
              </div>
            )}
            {props.fields.map((field, i) => {
              return (
                <Disclosure.Panel
                  key={field.title + i}
                  className='grid grid-cols-[minmax(min-content,.5fr)_minmax(300px,1fr)] my-4'
                >
                  {field.sectionTitle && (
                    <h3 className='col-span-2 text-lg border-b-4 mb-2 border-b-gray-100 uppercase'>
                      {field.sectionTitle}
                    </h3>
                  )}
                  <h3>{field.title}</h3>
                 
                  {field.type == 'checkbox' && (
                    <Checkbox
                      data={field}
                      register={register}
                      setValue={setValue}
                      nestedIndex={index}
                      fieldName={field.name}
                      fieldIndex={i}
                    />
                  )}
                
                </Disclosure.Panel>
              );
            })}
          </Disclosure>
        );
      })}
    </div>
  );
};

Checkbox JS

import React from 'react';

const Checkbox = ({  data,
  register,
  setValue,
  nestedIndex = null,
  fieldName = '',
  fieldIndex = null,}) => {
  return (
    <div className='flex flex-col justify-start items-start'>
      {data.fields.map((item, index) => {
            return (
              // eslint-disable-next-line react/jsx-key

              <div
                key={`${item.title}${index}`}
                className='flex flex-row items-center'
              >
                <label className='mr-2' htmlFor={item.name}>
                  {item.title}
                </label>
                <input
                  type={item.type}
                  name={`Defects[${nestedIndex}].fields[${fieldIndex}].fields[${index}].${item.name}`}
                  className={`${
                    item.type == 'textarea' && 'w-full'
                  } border border-gray-100 p-2`}
                  {...register(
                    `Defects[${nestedIndex}].fields[${fieldIndex}].fields[${index}].${item.name}`
                  )}
                />
              </div>
            );
          })}


Solution

  • I have passed the instance of useForm's hook to the child components then I have used it in the child components as you see in the checkbox file and it's working correctly now.