将 React Hook Form 与其他组件一起使用

Using React Hook Form with other component

我想用 react-hook-form 创建动态表单。

下面是我的代码。

我想创建一个对话框,用于在单独的组件中输入详细的配置文件,并在 MainForm 中使用它。 我可以显示 DetailForm,但其中输入的值没有反映出来。 提交时不包括DetailForm组件上的数据。

任何有关如何执行此操作的指导将不胜感激。

主窗体

import React from 'react';
import {
  useForm,
  Controller,
  useFieldArray
} from 'react-hook-form';
import {
  Button,
  TextField,
  List,
  ListItem,
  IconButton,
} from '@material-ui/core';
import DetailForm from '@components/DetailForm'
import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline';


function MainForm(props:any) {
  const { control, handleSubmit, getValues } = useForm({
    mode: 'onBlur',
    defaultValues: {
      profiles: [
        {
          firstName: '',
          lastName: '',
          email: '',
          phone: ''
        }
      ]
    }
  });
  const { fields, append, remove } = useFieldArray({
    control,
    name: 'profiles',
  });
  const onSubmit = () => {
    const data = getValues();
    console.log('data: ', data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <List>
        fields.map((item, index) => {
          return (
            <ListItem>
              <Controller
                name={`profiles.${index}.firstName`}
                control={control}
                render={({field}) => 
                  <TextField
                    { ...field }
                    label="First Name"
                  />
                }
              />
              <Controller
                name={`profiles.${index}.lastName`}
                control={control}
                render={({field}) => 
                  <TextField
                    { ...field }
                    label="Last Name"
                  />
                }
              />
              <DetailForm index={index} />
            </ListItem>
          )
        })
      </List>
      <IconButton onClick={() => append({})}>
        <AddCircleOutlineIcon />
      </IconButton>
      <Button
        type='submit'
      >
        SAVE
      </Button>
    </form>
  )
}

详细表格

import React from 'react';
import {
  Button,
  TextField,
  Dialog,
  DialogTitle,
  DialogContent,
  DialogActions,
} from '@material-ui/core';

export default function DetailForm(props: any) {
  const [dialogState, setDialogState] = React.useState<boolean>(false);
  const handleOpen = () => {
    setDialogState(true);
  };
  const handleClose = () => {
    setDialogState(false);
  };
  return (
    <>
      <Button
        onClick={handleOpen}
      >
        Set Detail Profile
      </Button>
      <Dialog open={dialogState}>
        <DialogTitle>Detail Profile</DialogTitle>
        <DialogContent>
          <TextField
            name={`profiles.${props.index}.email`}
            label="Email Address"
          />
          <TextField
            name={`profiles.${props.index}.phone`}
            label="Phone Number"
          />
        </DialogContent>
        <DialogActions>
          <Button onClick={handleClose}>
            Cancel
          </Button>
          <Button onClick={handleClose}>
            Add
          </Button>
        </DialogActions>
      </Dialog>
    </>
  )
}

您必须在 <Dialog /> 组件中注册这些字段 - 只需将 control 传递给它即可。使用 <Controller />useFieldArray 时设置内联 defaultValue 也很重要。来自 docs:

inline defaultValue is required when working with useFieldArray by integrating with the value from fields object.

另一件小事:您还应该将完整的 fieldId 传递给您的 <Dialog /> 组件,而不仅仅是传递 index。这样,在一个地方更改 fieldId 比编辑 <Dialog /> 组件中的所有字段会更容易。

主窗体

<ListItem key={item.id}>
  <Controller
    name={`profiles.${index}.firstName`}
    control={control}
    defaultValue=""
    render={({ field }) => (
      <TextField {...field} label="First Name" />
    )}
  />
  <Controller
    name={`profiles.${index}.lastName`}
    control={control}
    defaultValue=""
    render={({ field }) => (
      <TextField {...field} label="Last Name" />
    )}
  />
  <DetailForm control={control} fieldId={`profiles.${index}`} />
</ListItem>

DetailForm

export default function DetailForm({ control, fieldId }) {
  const [dialogState, setDialogState] = React.useState(false);
  const handleOpen = () => {
    setDialogState(true);
  };
  const handleClose = () => {
    setDialogState(false);
  };
  return (
    <>
      <Button onClick={handleOpen}>Set Detail Profile</Button>
      <Dialog open={dialogState}>
        <DialogTitle>Detail Profile</DialogTitle>
        <DialogContent>
          <Controller
            name={`${fieldId}.email`}
            control={control}
            defaultValue=""
            render={({ field }) => (
              <TextField {...field} label="Email Address" />
            )}
          />
          <Controller
            name={`${fieldId}.phone`}
            control={control}
            defaultValue=""
            render={({ field }) => (
              <TextField {...field} label="Phone Number" />
            )}
          />
        </DialogContent>
        <DialogActions>
          <Button onClick={handleClose}>Cancel</Button>
          <Button onClick={handleClose}>Add</Button>
        </DialogActions>
      </Dialog>
    </>
  );
}