admin管理员组

文章数量:1398794

I have a Form.js ponent which returns a form element. This form element contains a FormGroup ponent which takes props such as inputType, inputName, inputPlaceholder and in return renders an input field with a label. I am using react-hook-form for the validation of the input but I can't get it to work after extracting the input to a separate ponent. In my original code, I had my errors appear when the validation failed, but after extracting the label and input into their own ponent, this stopped working.

My original working code looks like this:

import { useState } from 'react';
import { useForm } from 'react-hook-form';
import FormGroup from './FormGroup';

const Form = () => {
    const { register, handleSubmit, formState: { errors } } = useForm();

    const onSubmit = async (data) => {
        console.log(data)
    };

    return (
        <form className="form-container" onSubmit={handleSubmit(onSubmit)} autoComplete="off" noValidate>
            <div className="form-group">
                <label className="form-label" htmlFor="firstName">
                    <p>First Name</p>
                    <p className="input-error">{errors.firstName && errors.firstName.message}</p>
                </label>
                <input type="text" name="firstName" placeholder="First Name" {...register("firstName", { required: 'Required ' })} />
            </div>

            <div className="form-group">
                <button type="submit">Submit</button>
            </div>
        </form>
    );
}

export default Form

Then I changed it to:

import { useState } from 'react';
import { useForm } from 'react-hook-form';
import FormGroup from './FormGroup';

const Form = () => {
    const { register, handleSubmit, formState: { errors } } = useForm();

    const onSubmit = async (data) => {
        console.log(data)
    };

    return (
        <form className="form-container" onSubmit={handleSubmit(onSubmit)} autoComplete="off" noValidate>
            <FormGroup
                inputType="text"
                inputName="firstName"
                inputPlaceholder="First Name">
            </FormGroup>

            <div className="form-group">
                <button type="submit">Submit</button>
            </div>
        </form>
    );
}

export default Form

And I extracted the label and input into:

import { useForm } from 'react-hook-form';

const FormGroup = (props) => {
    const { register, formState: { errors } } = useForm();

    return (
        <div className="form-group">
            <label className="form-label" htmlFor={props.inputName}>
                <p>{ props.inputPlaceholder }</p>
                <p className="input-error">{errors.firstName && errors.firstName.message}</p>
            </label>
            <input type={props.inputType} name={props.inputName} placeholder={props.inputPlaceholder} {...register(props.inputName, { required: true })} />
        </div>
    );
}

export default FormGroup

I have a Form.js ponent which returns a form element. This form element contains a FormGroup ponent which takes props such as inputType, inputName, inputPlaceholder and in return renders an input field with a label. I am using react-hook-form for the validation of the input but I can't get it to work after extracting the input to a separate ponent. In my original code, I had my errors appear when the validation failed, but after extracting the label and input into their own ponent, this stopped working.

My original working code looks like this:

import { useState } from 'react';
import { useForm } from 'react-hook-form';
import FormGroup from './FormGroup';

const Form = () => {
    const { register, handleSubmit, formState: { errors } } = useForm();

    const onSubmit = async (data) => {
        console.log(data)
    };

    return (
        <form className="form-container" onSubmit={handleSubmit(onSubmit)} autoComplete="off" noValidate>
            <div className="form-group">
                <label className="form-label" htmlFor="firstName">
                    <p>First Name</p>
                    <p className="input-error">{errors.firstName && errors.firstName.message}</p>
                </label>
                <input type="text" name="firstName" placeholder="First Name" {...register("firstName", { required: 'Required ' })} />
            </div>

            <div className="form-group">
                <button type="submit">Submit</button>
            </div>
        </form>
    );
}

export default Form

Then I changed it to:

import { useState } from 'react';
import { useForm } from 'react-hook-form';
import FormGroup from './FormGroup';

const Form = () => {
    const { register, handleSubmit, formState: { errors } } = useForm();

    const onSubmit = async (data) => {
        console.log(data)
    };

    return (
        <form className="form-container" onSubmit={handleSubmit(onSubmit)} autoComplete="off" noValidate>
            <FormGroup
                inputType="text"
                inputName="firstName"
                inputPlaceholder="First Name">
            </FormGroup>

            <div className="form-group">
                <button type="submit">Submit</button>
            </div>
        </form>
    );
}

export default Form

And I extracted the label and input into:

import { useForm } from 'react-hook-form';

const FormGroup = (props) => {
    const { register, formState: { errors } } = useForm();

    return (
        <div className="form-group">
            <label className="form-label" htmlFor={props.inputName}>
                <p>{ props.inputPlaceholder }</p>
                <p className="input-error">{errors.firstName && errors.firstName.message}</p>
            </label>
            <input type={props.inputType} name={props.inputName} placeholder={props.inputPlaceholder} {...register(props.inputName, { required: true })} />
        </div>
    );
}

export default FormGroup
Share Improve this question asked Apr 18, 2021 at 3:28 OnyxOnyx 5,78210 gold badges52 silver badges119 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 3

The Problem

You have multiple instances of the useForm hook.

Solution

Use the useForm hook only on the Form ponent and pass the errors object and register method as props to FormGroup

Form Component

import { useState } from 'react';
import { useForm } from 'react-hook-form';
import FormGroup from './FormGroup';

const Form = () => {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm();

  const onSubmit = async (data) => {
    console.log(data);
  };

  return (
    <form
      className="form-container"
      onSubmit={handleSubmit(onSubmit)}
      autoComplete="off"
      noValidate
    >
      <FormGroup
        inputType="text"
        inputName="firstName"
        inputPlaceholder="First Name"
        register={register}
        errors={errors}
      ></FormGroup>

      <div className="form-group">
        <button type="submit">Submit</button>
      </div>
    </form>
  );
};

export default Form;

FormGroup Component

import { useForm } from 'react-hook-form';

const FormGroup = (props) => {
  return (
    <div className="form-group">
      <label className="form-label" htmlFor={props.inputName}>
        <p>{props.inputPlaceholder}</p>
        <p className="input-error">
          {props.errors.firstName && props.errors.firstName.message}
        </p>
      </label>
      <input
        type={props.inputType}
        name={props.inputName}
        placeholder={props.inputPlaceholder}
        {...props.register(props.inputName, { required: true })}
      />
    </div>
  );
};

export default FormGroup;

The remended approach from the Documentation is as follows:

useFormContext This custom hook allows you to access the form context. useFormContext is intended to be used in deeply nested structures, where it would bee inconvenient to pass the context as a prop. https://react-hook-form./api/useformcontext

const Form = () => {
  const methods = useForm();
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = methods;

  const onSubmit = async (data) => {
    console.log(data);
  };

  return (
    <FormProvider {...methods} > 
      <form
        className="form-container"
        onSubmit={handleSubmit(onSubmit)}
        autoComplete="off"
        noValidate
      >
        <FormGroup
          inputType="text"
          inputName="firstName"
          inputPlaceholder="First Name">
        </FormGroup>

        <div className="form-group">
          <button type="submit">Submit</button>
        </div>
      </form>
    </FormProvider>
  );
};
const FormGroup = (props) => {
  const { register, errors } = useFormContext(); // retrieve all hook methods from parent
  return (
    <div className="form-group">
      <label className="form-label" htmlFor={props.inputName}>
        <p>{props.inputPlaceholder}</p>
        <p className="input-error">
          {errors.firstName && errors.firstName.message}
        </p>
      </label>
      <input
        type={props.inputType}
        name={props.inputName}
        placeholder={props.inputPlaceholder}
        {register(props.inputName, { required: true })}
      />
    </div>
  );
};

export default FormGroup;

本文标签: