React Hook Form with AntD Styling
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>
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;
Related videos on Youtube
Mel
Updated on June 04, 2022Comments
-
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 over 4 yearsThanks 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 over 4 yearsDo you want to post this issue at our spectrum channel? I am happy to assist there.
-
Bill over 4 yearsif you want to trigger validation, setValue('xxx', value, true) it's all been documented on the website
-
Mel over 4 yearsThanks @Bruce - I'll try again.
-
Mel over 4 yearsThanks for sharing - I'll have a look at this tonight. I'm still struggling to figure this out.
-
Firanolfind almost 4 yearsThank you, it works! This ugly workaround, because of antd. I definitely wont use ant next time.
-
Ryall about 3 yearsThis is now built into RHF, so you should use a Controller component to wrap it instead.
-
Dang Kien about 3 yearsHi, 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 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