Pirikara
Pirikara

Reputation: 353

Using React Hook Form with other component

I want to create dynamic form with react-hook-form.

enter image description here

Below is my code.

I want to create a dialog for entering a detailed profile in a separate component and use it in MainForm. I can display the DetailForm, but the values entered in it are not reflected. Data on the DetailForm component is not included when submitting.

Any guidance on how to do this would be greatly appreciated.

MainForm

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>
  )
}

DetailForm

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>
    </>
  )
}

Upvotes: 2

Views: 7179

Answers (1)

knoefel
knoefel

Reputation: 6949

You have to register those fields inside your <Dialog /> component - just pass control to it. It's also important to set an inline defaultValue when using <Controller /> and useFieldArray. From the docs:

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

One other minor thing: You should also pass the complete fieldId to your <Dialog /> component instead of just passing the index. This way it would be easier to change the fieldId in one place instead of editing all fields in your <Dialog /> component.

MainForm

<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>
    </>
  );
}

Edit React Hook Form - Basic (forked)

Upvotes: 2

Related Questions