React Hook Forms - 预填充字段数组不起作用

React Hook Forms - Pre-populate Fields Array Not Working

我是 React Hook Forms 的新手,除了在传入数据的编辑屏幕上使用 useFieldArray 显示 fields 之外,我一切正常。例如,我有以下:

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;

从上面的 console.log 中,我看到 ticketData 设置正确。例如,我有这个输出:

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

如果我注销 control 是什么,我也会注意到一切都是空的:

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

  }
 

然后,我有这个文件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;

问题是我的 console.log('fields are', fields) 一直告诉我 fields[]。我不明白我在这里缺少什么。我正在将使用 useForm 设置的 control 传递给我的 <LineItems> 组件。

我的 defaultValues 对象的键名称与 useFieldArray 配置的 name 匹配,但字段始终显示为空。我希望有人可以让我知道我错过了什么。

谢谢!

显然我缺少 reset 函数。根据文档,需要重置,因为所有默认值都在第一次渲染时缓存。

我能够通过将以下内容添加到我的 TicketForm 组件来解决它:

  useEffect(() => {
    if (ticket)
      reset({
        line_items_attributes: [...ticket.line_items],
        attachments_attributes: [...ticket.attachments],
      })
  }, [])