React Hook Form with AntD Styling

11,206

Solution 1

react-hook-form author here. Antd Input component doesn't really expose inner ref, so you will have to register during useEffect, and update value during onChange, eg:

const { register, setValue } = useForm();

useEffect(() => {
  register({ name: 'yourField' }, { required: true });
}, [])

<Input name="yourField" onChange={(e) => setValue('yourField', e.target.value)}

i have built a wrapper component to make antd component integration easier: https://github.com/react-hook-form/react-hook-form-input

import React from 'react';
import useForm from 'react-hook-form';
import { RHFInput } from 'react-hook-form-input';
import Select from 'react-select';

const options = [
  { value: 'chocolate', label: 'Chocolate' },
  { value: 'strawberry', label: 'Strawberry' },
  { value: 'vanilla', label: 'Vanilla' },
];

function App() {
  const { handleSubmit, register, setValue, reset } = useForm();

  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      <RHFInput
        as={<Select options={options} />}
        rules={{ required: true }}
        name="reactSelect"
        register={register}
        setValue={setValue}
      />
      <button
        type="button"
        onClick={() => {
          reset({
            reactSelect: '',
          });
        }}
      >
        Reset Form
      </button>
      <button>submit</button>
    </form>
  );
}

Solution 2

This is my working approach:

const Example = () => {

 const { control, handleSubmit, errors } = useForm()

  const onSubmit = data => console.log(data)
  console.log(errors)

  return (
    <Form onSubmit={handleSubmit(onSubmit)}>
      <Controller
        name="email"
        control={control}
        rules={{ required: "Please enter your email address" }}
        as={
          <Form.Item
            label="name"
            validateStatus={errors.email && "error"}
            help={errors.email && errors.email.message}
          >
            <Input />
          </Form.Item>
        }
      />
      <Button htmlType="submit">Submit</Button>
    </Form>
  )
}

Solution 3

On writing such code:

<Input
  name="subtitle"
  placeholder="Add a subtitle"
  ref={register({ required: true })}
/>

You assume that Input reference is bound to input, but that's not true.

In fact, you need to bind it to inputRef.input.

You can check it with the next code:

const App = () => {
  const inputRef = useRef();
  const inputRefHtml = useRef();

  useEffect(() => {
    console.log(inputRef.current);
    console.log(inputRefHtml.current);
  });

  return (
    <FlexBox>
      <Input ref={inputRef} />
      <input ref={inputRefHtml} />
    </FlexBox>
  );
};
# Logs
Input {props: Object, context: Object, refs: Object, updater: Object, saveClearableInput: function ()…}

<input></input>

Edit Q-58703615-3rdPartyInAnt

Note that antd is a complete UI library (using 3rd party "helpers" should "turn a red light"), in particular, Form has a validator implemented, you can see a variety of examples in docs.

Solution 4

In Ant Design v4.x + react-hook-form v6.x. We can implement as normally

import { useForm, Controller, SubmitHandler } from 'react-hook-form';
import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';

import { useIntl } from 'react-intl';
import { Input, Button, Form } from 'antd';

const SignInSchema = yup.object().shape({
  email: yup.string().email().required(),
  password: yup.string().required('required').min(6, 'passwordMin'),
});

interface PropTypes {
  defaultValues?: {
    email: string;
    password: string;
  };
  handleFormSubmit: SubmitHandler<{ email: string; password: string }>;
}

function SignInForm({ defaultValues, handleFormSubmit }: PropTypes) {
  const intl = useIntl();
  const { handleSubmit, control, errors } = useForm({
    defaultValues,
    resolver: yupResolver(SignInSchema),
  });

  return (
    <Form onFinish={handleSubmit(handleFormSubmit)}>
      <Form.Item
        validateStatus={errors && errors['email'] ? 'error' : ''}
        help={errors.email?.message}
      >
        <Controller
          as={Input}
          name="email"
          autoComplete="email"
          control={control}
          placeholder={intl.formatMessage({ id: 'AUTH_INPUT_EMAIL' })}
        />
      </Form.Item>
      <Form.Item
        validateStatus={errors && errors['password'] ? 'error' : ''}
        help={errors.password?.message}
      >
        <Controller
          as={Input}
          name="password"
          type="password"
          control={control}
          autoComplete="new-password"
          defaultValue=""
          placeholder={intl.formatMessage({ id: 'AUTH_INPUT_PASSWORD' })}
        />
      </Form.Item>

      <Button type="primary" htmlType="submit">
        {intl.formatMessage({ id: 'SIGN_IN_SUBMIT_BUTTON' })}
      </Button>
    </Form>
  );
}

export default SignInForm;

Share:
11,206

Related videos on Youtube

Mel
Author by

Mel

Updated on June 04, 2022

Comments

  • Mel
    Mel almost 2 years

    I'm trying to figure out how to use react-hook-form with antd front end.

    I have made this form and it seems to be working (it's part 1 of a multipart form wizard) except that the error messages do not display.

    Can anyone see what I've done wrong in merging these two form systems?

    I'm not getting any errors, but I think I have asked for both form fields to be required but if I press submit without completing them the error messages are not displayed.

    import React from "react";
    import useForm from "react-hook-form";
    import { BrowserRouter as Router, Route } from "react-router-dom";
    import { StateMachineProvider, createStore } from "little-state-machine";
    import { withRouter } from "react-router-dom";
    import { useStateMachine } from "little-state-machine";
    
    import updateAction from "./updateAction";
    import { Button, Form, Input,  Divider, Layout, Typography, Skeleton, Switch, Card, Icon, Avatar } from 'antd';
    
    
    const { Content } = Layout 
    const { Text, Paragraph } = Typography;
    const { Meta } = Card;
    
    createStore({
      data: {}
    });
    
    const General = props => {
      const { register, handleSubmit, errors } = useForm();
      const { action } = useStateMachine(updateAction);
      const onSubit = data => {
        action(data);
        props.history.push("./ProposalMethod");
      };
    
    
      return (
    
          <div>
    
            <Content
              style={{
                background: '#fff',
                padding: 24,
                margin: "auto",
                minHeight: 280,
                width: '70%'
              }}
            >
            <Form onSubmit={handleSubmit(onSubit)}>
    
              <h2>Part 1: General</h2>
                <Form.Item label="Title" >
                  <Input 
                    name="title" 
                    placeholder="Add a title" 
                    ref={register({ required: true })} 
                  />
                  {errors.title && 'A title is required.'}
                </Form.Item>
                <Form.Item label="Subtitle" >
                  <Input 
                    name="subtitle" 
                    placeholder="Add a subtitle" 
                    ref={register({ required: true })} 
                  />
                  {errors.subtitle && 'A subtitle is required.'}
                </Form.Item>
                <Form.Item>
                  <Button type="secondary" htmlType="submit">
                    Next
                  </Button>
                </Form.Item>
    
            </Form>
    
            </Content>
          </div>  
      );
    };
    
    export default withRouter(General);
    
  • Mel
    Mel over 4 years
    Thanks Bruce. Not sure I follow this. Error messages from antd work on the first step of a multistep form, but not subsequent steps. I'll keep trying and update this page if I figure something out.
  • Bill
    Bill over 4 years
    Do you want to post this issue at our spectrum channel? I am happy to assist there.
  • Bill
    Bill over 4 years
    if you want to trigger validation, setValue('xxx', value, true) it's all been documented on the website
  • Mel
    Mel over 4 years
    Thanks @Bruce - I'll try again.
  • Mel
    Mel over 4 years
    Thanks for sharing - I'll have a look at this tonight. I'm still struggling to figure this out.
  • Firanolfind
    Firanolfind almost 4 years
    Thank you, it works! This ugly workaround, because of antd. I definitely wont use ant next time.
  • Ryall
    Ryall about 3 years
    This is now built into RHF, so you should use a Controller component to wrap it instead.
  • Dang Kien
    Dang Kien about 3 years
    Hi, when adding the validateStatus & help props in Form.Item, I got the error index.js:1 Warning: findDOMNode is deprecated in StrictMode. findDOMNode was passed an instance of DomWrapper which is inside StrictMode. Instead, add a ref directly to the element you want to reference. Learn more about using refs safely here: reactjs.org/link/strict-mode-find-node. Please let me know how to fix it. Many thanks!
  • Phat Tran
    Phat Tran almost 3 years
    @ĐặngKiên this is an issue of the Antd Design, they might need to do a big changes for fixing this in the next version github.com/ant-design/ant-design/issues/27921