Home > Net >  How can I use useForm's register with dynamic parameter?
How can I use useForm's register with dynamic parameter?

Time:11-30

I want to create a form by using react-hook-form, but I am having trouble with it. As I found, there was a big change at v7 so the code I used as a reference is not working. I tried to modify it, and I figured out the problem is with the registering, but I cannot pass that name parameter dynamically.

My main component.

import React from 'react';
import i18next from 'i18next';

import { Button, Grid, Typography } from '@material-ui/core';
import { useForm, FormProvider } from 'react-hook-form';
import { Link } from 'react-router-dom';

import FormInput from './CustomTextField'

const AddressForm = ({ next }) => {
    const methods = useForm();

    return (
        <>
            <Typography variant="h6" gutterBottom>
                {i18next.t('shipping_address')}
            </Typography>
            <FormProvider {...methods}>
                <form onSubmit={methods.handleSubmit((data) => {
                    console.log(data);
                    next({ ...data })})}>
                    <Grid container spacing={3}>
                        <FormInput required register={methods.register} name='lastName' label={i18next.t('last_name')} />
                        <FormInput required register={methods.register} name='firstName' label={i18next.t('first_name')} />
                        <FormInput required register={methods.register} name='email' label={i18next.t('mail')} />
                        <FormInput required register={methods.register} name='zip' label={i18next.t('zip_code')} />
                        <FormInput required register={methods.register} name='city' label={i18next.t('city')} />
                        <FormInput required register={methods.register} name='address1' label={i18next.t('address_1')} />
                    </Grid>
                    <br />
                    <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                        <Button component={Link} to="/cart" variant="outlined">
                            {i18next.t('back_to_cart')}
                        </Button>
                        <Button type="submit" variant="contained" color="primary">
                            {i18next.t('next_step')}
                        </Button>
                    </div>
                </form>
            </FormProvider>
        </>
    )
}

export default AddressForm

CustomTextField component:

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

const CustomTextField = ({ name, label, register, required}) => {
    const { control } = useFormContext();

    return (
        <Grid item xs={12} sm={6}>
            <Controller 
                control={control}
                name={name}
                render = {(field) => (
                    <TextField
                        {...register({name})} // <--- It is not working like this
                        label={label}
                        required={required}
                    />
                )}
            />
        </Grid>
    )
}

export default CustomTextField

By the doc: https://react-hook-form.com/api/useform/register

it takes the input field's name as a parameter, if I pass it in as a string, it works fine. How can I pass the name's value as a parameter to the register() function?

CodePudding user response:

The problem is you're mixing RHF's <Controller /> and register. As Mui's <TextField /> is an external controlled component you should use <Controller />. Check the docs here for more info.

const CustomTextField = ({ name, label, register, required}) => {
    const { control } = useFormContext();

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

If you really want to use register here, you have to remove the wrapping <Controller /> and pass a name as a string instead as an object like you are doing right now. But i would recommend to use <Controller /> as with register you are losing the functionality of setting up the correct ref for your <TextField /> input element as it is linked via the inputRef prop instead of using ref which RHF register uses.

const CustomTextField = ({ name, label, register, required}) => {
    const { control } = useFormContext();

    return (
        <Grid item xs={12} sm={6}>
            <TextField
                {...register(name)}
                label={label}
                required={required}
            />
        </Grid>
    )
}
  • Related