hegonen
hegonen

Reputation: 123

react-hook-form Controller issues

Hi im trying to do one form with react-hook-form and material-ui. I don't want to write Controller every time for all TextFields. Because of that i declare it in another file and call it in my form but its not working i didn't understand why, because in some videos that i watched is working. What is the problem and how i can fix it ?

Form Field

import React from 'react'
import { TextField, Grid } from '@material-ui/core'
import { useForm, Controller, useFormContext  } from 'react-hook-form'

const FormField = ({name, label}) => {
const { control } = useForm()

return (
    <Grid item xs={12} sm={6} >
        <Controller 
            render = {({field}) => 
            <TextField 
                {...field}        
                label={label} required
            />}
            name={name}
            control = {control}
            defaultValue=""
        />
    </Grid>
)
}


export default FormField

Adress Form

import React from 'react'
import { InputLabel, Select, MenuItem, Button, Grid, Typography, TextField } from '@material-ui/core'
import { useForm, FormProvider, Controller } from 'react-hook-form'
import FormField from './FormField'
import { Link } from 'react-router-dom'

const AdressForm = ({next}) => {
const {handleSubmit, control} = useForm()
return (
    <>
        <Typography variant="h6" gutterBottom>Shipping Address </Typography>
        
            <form onSubmit={handleSubmit((data) => console.log(data) )}>
                <Grid container spacing={3}>
                    <FormField name='firstName' label='First Name' required='required'/>
                    <FormField name='lastName' label='Last Name' />
                    <FormField name='email' label='Email' />
                    <FormField name='phoneNumber' label='Phone Number' />
                </Grid>
                <br/>
                <div style={{ display: 'flex', justifyContent: 'space-between'}}>
                     <Button component={Link} to="/cart" variant="outlined">Back to Cart</Button>
                     <Button type="submit" variant="contained" color="primary">Next</Button>
                </div>
            </form>
        
    </>
)
}

export default AdressForm

Upvotes: 3

Views: 8024

Answers (1)

NearHuscarl
NearHuscarl

Reputation: 81310

You must use one useForm hook for each form, in your code, you call useForm in every Field components, creating multiple independent form states, which leads to unexpected result.

What you need to do is to call useForm in the parent element and pass the dependencies (register, formState, error...) down the child components, so your form can have one unified state. If you have a deeply nested components, you can use useFormContext to pass the form context to the nested children easily:

import React from "react";
import { useForm, FormProvider, useFormContext } from "react-hook-form";

export default function App() {
  const methods = useForm();
  const onSubmit = data => console.log(data);

  return (
    <FormProvider {...methods} > // pass all methods into the context
      <form onSubmit={methods.handleSubmit(onSubmit)}>
        <NestedInput />
        <input type="submit" />
      </form>
    </FormProvider>
  );
}

function NestedInput() {
  const { register } = useFormContext(); // retrieve all hook methods
  return <input {...register("test")} />;
}

Upvotes: 5

Related Questions