0

I'm new to React Hook Forms, and I have everything working except showing the fields using useFieldArray on an edit screen where the data is passed in. For example, I have the following:

TicketForm.js

import { useForm, useFieldArray, Controller } from 'react-hook-form';
import {
  CCard,
  CCardHeader,
  CCollapse,
  CCardBody,
  CRow,
  CCol,
  CSidebar,
  CSpinner,
  CForm,
  CFormGroup,
  CInputCheckbox,
  CFormText,
  CTextarea,
  CInput,
  CInputFile,
  CLabel,
  CButton,
  CButtonToolbar,
  CButtonGroup,
  CSelect
} from '@coreui/react';
import CIcon from '@coreui/icons-react';
import { useSelector } from 'react-redux';
import { useHistory } from 'react-router-dom';
import Input from '../../base/forms/input';
import Select from '../../base/forms/select';
import TextArea from '../../base/forms/text_area';
import Checkbox from '../../base/forms/check_box';
import LineItems from './line_items';
import Attachments from './attachment_items';

const TicketHookForm = props => {
  let ticket = props.ticket;
  const ticketData = Object.keys(ticket).length ? {
    defaultValues: {
      line_items_attributes: [...ticket.line_items],
      attachments_attributes: [...ticket.attachments]
    }
  } : {};

  console.log('ticket data is', ticketData);
  const { setValue, control, handleSubmit, watch } = useForm(ticketData);

  const stores = useSelector(state => state.referenceData.stores);
  const categories = useSelector(state => state.referenceData.categories);
  const history = useHistory();

  const formatReferenceData = (data, selectedId) => {
    return data.map(item => <option key={item.id} value={item.id} selected={item.id === selectedId}>{item.name}</option>);
  }

  const submit = (data) => {
    console.log('data', data);
    props.onSubmit(data);
  }

  return (
    <CRow>
      <CCol xs={12}>
        <CForm onSubmit={(e) => {
            e.preventDefault();
            handleSubmit(submit)()
        }} method="post" encType="multipart/form-data">
              <CFormGroup row>
                <CCol xs={3}>
                  <CLabel htmlFor="line_items">Line Items</CLabel>
                </CCol>
                <CCol xs={9}>
                  <LineItems control={control} />
                </CCol>
              </CFormGroup>
            </CCardBody>
          </CCard>
          <CCard>
            <CCardHeader>Additional Information</CCardHeader>
            <CCardBody>
              <CFormGroup row>
                <CCol xs={3}>
                  <CLabel htmlFor="attachments">Attachments</CLabel>
                </CCol>
                <CCol xs={9}>
                  <Attachments control={control} setValue={setValue} />
                </CCol>
              </CFormGroup>
            </CCardBody>
          </CCard>
          <CCard>
            <CCardHeader>
              <CButton type="submit" color="primary"><CIcon name="cil-user" /> {props.ticketId ? 'Update' : 'Create'} Check Request</CButton>
              &nbsp;<CButton type="reset" color="danger" onClick={() => history.goBack()}><CIcon name="cil-ban" /> Cancel</CButton>
            </CCardHeader>
          </CCard>
        </CForm>
      </CCol>
    </CRow>
  )
}

TicketHookForm.defaultProps = {
  ticket: {}
}

export default TicketHookForm;

From the console.log above, I see that ticketData is being set properly. For example, I have this output:

ticket data is {
  defaultValues: 
    attachments_attributes: [{…}]
    line_items_attributes: [{id: 8, description: "333", store: "Holman Cadillac", store_id: 2, account: "1", …}]
  }
}

If I logged out what control is, I also notice everything is empty:

control is {
  fieldArrayDefaultValuesRef: {
    current: {
      attachments_attributes: [],
      line_items_attributes: []
    }

  }
 

Then, I have this file LineItems.js

import { useFieldArray } from 'react-hook-form';
import {
  CFormGroup,
  CRow,
  CLabel,
  CButton,
  CCol
} from '@coreui/react';
import Input from '../../base/forms/input';
import Select from '../../base/forms/select';
import { useSelector } from 'react-redux';

const LineItems = props => {
  const { fields, append, prepend, remove, swap, move, insert } = useFieldArray({
    control: props.control,
    name: 'line_items_attributes'
  });

  console.log('fields are ', fields);
  const stores = useSelector(state => state.referenceData.stores);

  const formatStores = () => {
    return stores.map(item => <option key={item.id} value={item.id}>{item.name}</option>);
  }

  return (
    <section className="line-item">
      {fields.map((item, index) => (
        <div key={item.id}>
          <CFormGroup row>
            <CCol xs={4}>
              <CLabel htmlFor="store">Store</CLabel>
              <Select control={props.control} defaultValue={item.store_id} id="store-select" name={`line_items_attributes[${index}].store_id`} options={formatStores(stores)} control={props.control} />
            </CCol>
            <CCol xs={4}>
              <CLabel htmlFor="description">Description</CLabel>
              <Input control={props.control} defaultValue={item.description} id="description" name={`line_items_attributes[${index}].description`} />
            </CCol>
            <CCol xs={4}>
              <CLabel htmlFor="control_1">Control 1</CLabel>
              <Input control={props.control} defaultValue={item.control} id="control_1" name={`line_items_attributes[${index}].control`} />
            </CCol>
          </CFormGroup>
          <CFormGroup row>
            <CCol xs={4}>
              <CLabel htmlFor="account">Account</CLabel>
              <Input control={props.control} defaultValue={item.account} name={`line_items_attributes[${index}].account`} id="account" />
            </CCol>
            <CCol xs={4}>
              <CLabel htmlFor="amount">Amount</CLabel>
              <Input control={props.control} defaultValue={item.amount} type="number" id="amount" name={`line_items_attributes[${index}].amount`} />
            </CCol>
            <CCol xs={4}>
              <CLabel htmlFor="control_2">Control 2</CLabel>
              <Input control={props.control} defaultValue={item.control2} name={`line_items_attributes[${index}].control2`} id="control_2" />
            </CCol>
          </CFormGroup>
          <CRow>
            <CCol xs={12} className="text-right"><CButton color="danger" onClick={() => remove(index)}>Remove</CButton></CCol>
          </CRow>
        </div>
      ))}
      <CButton color="primary" variant="outline" onClick={() => append({})}>Add Line</CButton>
    </section>
  )
}

export default LineItems;

Input.js

import {
  CInput
} from '@coreui/react';
import { Controller } from 'react-hook-form';

const Input = (props) => {

  return (
    <Controller 
      control={props.control}
      name={props.name}
      defaultValue={props.defaultValue}
      render={({field: { onChange, onBlur, value, ref } }) => {
        return <CInput defaultValue={props.defaultValue} id={props.id} onChange={onChange} />
      }}
    />
  )
}

export default Input;

The problem is my console.log('fields are', fields) keeps telling me fields is []. I'm not understanding what I'm missing here. I'm passing the control that I set up with useForm to my <LineItems> component.

I have the key names of my defaultValues objects match the name of the useFieldArray configuration, yet fields keeps showing up empty. I'm hoping somebody can let me know what I'm missing.

Thank you!

1 Answer 1

1

Apparently I was missing the reset function. According to documentation, reset is required because all defaultValues are cached on first render.

I was able to solve it by adding the following to my TicketForm component:

  useEffect(() => {
    if (ticket)
      reset({
        line_items_attributes: [...ticket.line_items],
        attachments_attributes: [...ticket.attachments],
      })
  }, [])
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.