I created a model for a form which maps throught it to create form using react-hook-form
when i submit form for the first time, everything is Okay. but for the other times, when i paste a value into inputs, validation not work correctly(it shows required error but the input is not empty)
this is model:
export const formData = [
{
id: 1,
type: "text",
labelRequired: true,
label: "name",
shape: "inline",
placeholder: "name",
name: "nameOne",
validation: {
required: true,
minLength: 8,
},
size: 6,
},
{
id: 2,
type: "text",
labelRequired: true,
label: "family",
shape: "inline",
placeholder: "family",
name: "nameTwo",
validation: {
required: true,
minLength: 8,
},
size: 6,
},
{
id: 7,
type: "checkbox",
label: "Sample",
shape: "checkbox",
placeholder: "placeholder",
name: "checkbox_btn",
data: [
{
id: 41,
inputId: "inline-form-1",
label: "1",
},
],
size: 12,
},
];
the map method to create form based on model:
const Sample = () => {
const {
register,
handleSubmit,
formState: { errors },
reset,
} = useForm({
mode: "onBlur",
});
const submitHandler = (data, e) => {
e.target.reset();
console.log(data);
reset();
};
const mapForm = formData.map((item) => {
if (item.shape === "inline") {
return (
<InputContainer
key={item.id}
register={{
...register(item.name, {
required: item.validation.required,
minLength: item.validation.minLength,
}),
}}
validation={item.validation}
placeholder={item.placeholder}
label={item.label}
size={item.size}
error={errors[item.name]}
/>
);
}
if (item.shape === "checkbox") {
return (
<CheckBoxContainer
key={item.id}
label={item.label}
data={item.data}
register={{ ...register("medium", { required: true }) }}
error={errors.medium}
/>
);
}
});
return (
<Fragment>
<Breadcrumb parent="Dashboard" title="Default" />
<Container fluid={true}>
<Row>
<Col sm="12">
<Card>
<CardHeader>
<h5>Sample Card</h5>
</CardHeader>
<CardBody>
<Form onSubmit={handleSubmit(submitHandler)}>
<Row>{mapForm}</Row>
<Button type="submit" className="m-t-40">
Submit
</Button>
</Form>
</CardBody>
</Card>
</Col>
</Row>
</Container>
</Fragment>
);
};
export default Sample;
Also there are some container components for wrapping inputs like:
const InputContainer = ({
id,
register,
error,
label,
labelRequired,
size,
type,
placeholder,
validation,
}) => {
return (
<Col lg={size} style={{ marginTop: "-10px" }}>
<FormGroup>
<Label className={`col-form-label`}>
{label} {labelRequired && <span></span>}
</Label>
<Input
className="form-control"
type={type}
placeholder={placeholder}
{...register}
/>
{error && error.type === "required" && (
<p className="p-16 text-danger">this filed is required</p>
)}
</FormGroup>
</Col>
);
};
export default InputContainer;
I have not looked that deep into it to find out if this happens from a simple misuse of the useForm() hook, but here's what I've figured out.
According to the source code, register() returns the following interface (unrelated stuff omitted):
type UseFormRegisterReturn<...> = {
onChange: ChangeHandler;
onBlur: ChangeHandler;
ref: RefCallBack;
...
}
As you can see, this means only the onChange() and onBlur() events are being registered. onChange() is the one we're interested in.
After some quick testing, I've realized that for some unknown reason (maybe a browser bug? no idea), in certain conditions onChange() doesn't trigger when pasting text with CTRL+V.
Luckily, the regular onInput() event still triggers, so we can simply define this event using the register.onChange() handler:
<Input
className="form-control"
type={type}
placeholder={placeholder}
{...register}
onInput={register && register.onChange}
// TypeScript
// onInput={register?.onChange}
/>
If onInput() is already being used:
<Input
className="form-control"
type={type}
placeholder={placeholder}
{...register}
onInput={e => {
doOtherStuff(e);
if (register && register.onChange) {
register.onChange(e);
}
// TypeScript
// register?.onChange(e);
}
/>
Related
Currently I am using semantic ui css multiple dropdown validation. How do I validate the dropdown so that user has to choose at least ONE option before submitting ?
Right now the current output is that if I submit the form WITHOUT choosing any option, the error message appears.
However, if I choose an option and the submit, the error message still appears.
Following is my code.
import React, { useState } from 'react'
import { Button, Checkbox, Form, Dropdown } from 'semantic-ui-react'
//validation dependencies
import { useForm, Controller } from "react-hook-form";
import { yupResolver } from '#hookform/resolvers/yup';
import * as Yup from 'yup';
function TestForm() {
const validationSchema = Yup.object().shape({
firstName: Yup.string()
.required('firstName is required')
.min(6, 'firstName must be at least 6 characters')
,
options: Yup
.object()
.shape({
key: Yup.number().required("key is required (from label)"),
value: Yup.string().required("Value is required")
})
.nullable() // for handling null value when clearing options via clicking "x"
.required("options is required (from outter null check)")
});
const formOptions = { mode: 'all', reValidateMode: 'onChange', resolver: yupResolver(validationSchema) };
const { register, handleSubmit, reset, formState: { errors }, watch, setValue, control } = useForm(formOptions);
const [stateOptions, setStateOptions] = useState([
{
key: 1,
text: 'aaaa',
value: 'a'
},
{
key: 2,
text: 'bbbb',
value: 'b'
},
{
key: 3,
text: 'cccc',
value: 'c'
},
]);
console.log(watch())//For Debugging
const onSubmitHandler = (data) => {
console.log({ data });
setValue('firstName', 'dash', { shouldDirty: true, shouldValidate: true })
}
return (
<div>
<Form reply onSubmit={handleSubmit(onSubmitHandler)}>
<Form.Field>
<label>First Name</label>
<input placeholder='First Name' name="firstName" {...register('firstName')} />
<div className="invalid-feedback">{errors.firstName?.message}</div>
</Form.Field>
<Form.Field>
<Controller
name="options"
control={control}
render={({ field }) => (
<Dropdown
{...field}
placeholder='State'
fluid
multiple
search
selection
options={stateOptions}
/>
)}
/>
<div className="invalid-feedback">{errors.options?.message || errors.options?.value.message}</div>
</Form.Field>
<Form.Field>
<Button type='submit'>Submit</Button>
</Form.Field>
</Form>
</div>
)
}
export default TestForm
Since Dropdown component in semantic-ui-react doesn't support ref attribute, so you need to controll the value of DropDown by yourself, here is an example you can try on it:
const options = [
{
key: 1,
text: 'aaaa',
value: 'a',
},
{
key: 2,
text: 'bbbb',
value: 'b',
},
{
key: 3,
text: 'cccc',
value: 'c',
},
]
function TestForm() {
const validationSchema = Yup.object().shape({
firstName: Yup.string()
.required('firstName is required')
.min(6, 'firstName must be at least 6 characters'),
options: Yup.array()
.of(Yup.object()
.shape({
key: Yup.number().required('key is required (from label)'),
value: Yup.string().required('Value is required'),
}))
.test(
"required",
"options is required",
(value) => Array.isArray(value) && value.length > 0
),
});
const formOptions = {
mode: 'all',
reValidateMode: 'onChange',
resolver: yupResolver(validationSchema),
};
const {
register,
handleSubmit,
formState: { errors },
setValue,
control,
} = useForm(formOptions);
const onSubmitHandler = (data) => {
console.log(data);
};
return (
<div>
<Form reply onSubmit={handleSubmit(onSubmitHandler)}>
<Form.Field>
<label>First Name</label>
<input
placeholder="First Name"
name="firstName"
{...register('firstName')}
/>
<div className="invalid-feedback">{errors.firstName?.message}</div>
</Form.Field>
<Form.Field>
<Controller
name="options"
control={control}
render={({ field }) => {
let { value, ...other } = field;
return (
<Dropdown
{...other}
placeholder="State"
fluid
multiple
search
selection
options={options}
value={Array.isArray(value) ? value.map(v => v.value) : []}
onChange={(e,{value})=> {
const values = value.map(v => options.find(item => item.value == v));
setValue('options', values)
}}
/>
);
}}
/>
<div className="invalid-feedback">
{errors.options?.message || errors.options?.value.message}
</div>
</Form.Field>
<Form.Field>
<Button type="submit">Submit</Button>
</Form.Field>
</Form>
</div>
);
}
thanks for reading this.
I am trying to build a form using Formik. And it includes a FieldArray inside a FieldArray.
For some reason, setFieldValue works as I can console.log the correct e.target.name and e.target.value
The problem is upon submitting the form, all the values from the inputs are not where they supposed to be. The expected behavior is the values should be inside of exclusionGroups, not outside.
Would anyone be able to give me some insight ? I've been stuck on this the whole day and feels like my head is going to explode.
Expected:
exclusionGroups: [
{
exclusion: [
{
param: 'delayMin',
operator: '<',
value: 'test1',
},
{
param: 'airlineCode',
operator: '>',
value: 'test2',
},
],
},
{
exclusion: [
{
param: 'flightNumber',
operator: '=',
value: 'test3',
},
],
},
],
Reality:
exclusionGroups: (2) [{…}, {…}]
group-1-operator-1: "<"
group-1-operator-2: ">"
group-1-param-1: "delayMin"
group-1-param-2: "airlineCode"
group-1-value-1: "test1"
group-1-value-2: "test2"
group-2-operator-1: "="
group-2-param-1: "flightNumber"
group-2-value-1: "test3"
My Code:
Index.tsx
type ExclusionRuleValues = {
param: string;
operator: string;
value: string;
};
interface ExclusionGroupProps {
exclusionRules?: ExclusionRuleValues[];
}
const Exclusion = ({ data, type }: any) => {
const onSubmit = (values: any) => {
console.log(values);
};
const initialValues = {
exclusionGroups: [
{
exclusion: [
{
param: 'group1',
operator: 'group1',
value: 'group1',
},
],
},
{
exclusion: [
{
param: 'group2',
operator: 'group2',
value: 'group2',
},
],
},
],
};
const emptyGroup = {
exclusion: [
{
param: '',
operator: '',
value: '',
},
],
};
return (
<React.Fragment>
<Formik
enableReinitialize
onSubmit={onSubmit}
initialValues={initialValues}
render={(formProps) => {
const { values, handleSubmit, submitForm } = formProps;
const { exclusionGroups } = values;
const setFieldValue = (e: ChangeEvent<HTMLInputElement>) => {
return formProps.setFieldValue(e.target.name, e.target.value);
};
return (
<React.Fragment>
<Header>
Model will return excluded message code if the following condition is true.
<Button onClick={submitForm} color="primary">
Save Changes
</Button>
</Header>
<Form onSubmit={handleSubmit}>
<FieldArray
name="exclusionGroups"
render={(arrayHelper) => (
<React.Fragment>
{exclusionGroups.map((exclusionRulesGroup: any, index: number) => {
return (
<TargetFields
type={type}
group={index + 1}
key={`field-${index}`}
name={`${arrayHelper.name}.${index}`}
setFieldValue={setFieldValue}
/>
);
})}
<AddNewGroupButton type="button" onClick={() => arrayHelper.push(emptyGroup)}>
+ New Group
</AddNewGroupButton>
</React.Fragment>
)}
/>
</Form>
</React.Fragment>
);
}}
/>{' '}
</React.Fragment>
);
};
export default Exclusion;
TargetFields.tsx
interface TargetFieldsProps {
group: number;
name: string;
type: string;
data?: ExclusionRuleValues[];
setFieldValue: (e: ChangeEvent<HTMLInputElement>) => void;
}
const TargetFields = ({ group, data, name, type, setFieldValue }: TargetFieldsProps) => {
const emptyTarget = {
param: '',
operator: '',
value: '',
};
return (
<React.Fragment>
<Field name={name}>
{(fieldProps: any) => (
<React.Fragment>
<ExclusionGroupHeader>
<b>Group {group}</b>
</ExclusionGroupHeader>
<Wrapper>
<FieldArray
name={`${fieldProps.field.name}.exclusion`}
key={`exclusion-${group}`}
render={(targetHelper) => (
<React.Fragment>
{fieldProps.field.value.exclusion.map(
(target: ExclusionRuleValues, key: number) => {
const { param, operator, value } = target;
return (
<ExclusionRuleGroup key={`group-${key}`}>
<ExclusionRuleHeader>
<b>Target {key + 1}</b>
<DeleteButton type="button" onClick={() => targetHelper.remove(key)}>
remove
</DeleteButton>
</ExclusionRuleHeader>
<StyledRow>
<CCol sm="4">
<Select
onChange={setFieldValue}
// value={param}
label="Params"
name={`group-${group}-param-${key + 1}`}
options={
type === 'input' ? InputExclusionParams : OutputExclusionParams
}
placeholder="Operator"
/>
</CCol>
<CCol sm="4">
<Select
onChange={setFieldValue}
// value={operator}
label="Operator"
name={`group-${group}-operator-${key + 1}`}
options={SelectOptions}
placeholder="Operator"
/>
</CCol>
<CCol sm="4">
<Input
onChange={setFieldValue}
// value={value}
label="Value"
name={`group-${group}-value-${key + 1}`}
type="text"
placeholder="Value"
/>
</CCol>
</StyledRow>
</ExclusionRuleGroup>
);
}
)}
<AddNewRuleButton type="button" onClick={() => targetHelper.push(emptyTarget)}>
+ New Rule
</AddNewRuleButton>
</React.Fragment>
)}
/>
</Wrapper>
</React.Fragment>
)}
</Field>
</React.Fragment>
);
};
export default TargetFields;
The problem is when you pass the name property to your form's inputs.
In the component TargetFields you are passing the name like name={`group-${group}-operator-${key + 1}`} so formik think you want to store that value in the property that will result from that string, e.g. group-1-operator-1 and that is why it's going of the object you want, but going in a property with that name.
If you want to use nested objects / arrays, you need to concatenate the name with object with want using . or [${index}], just like you did in here
<TargetFields
type={type}
group={index + 1}
key={`field-${index}`}
name={`${arrayHelper.name}.${index}`} // You use the . with the name and the index.
setFieldValue={setFieldValue} // It could also be using [] instead of . like this => `${arrayHelper.name}[${index}]`
/>
and like here
<FieldArray
name={`${fieldProps.field.name}.exclusion`} // You use the . with the name and the property of the object you want
key={`exclusion-${group}`}
render={(targetHelper) => ( ... )
/>
So to solve you problem, you need to change the following.
<StyledRow>
<CCol sm="4">
<Select
onChange={setFieldValue}
// value={param}
label="Params"
name={`${targetHelper}[${key}].param`}
options={
type === 'input' ? InputExclusionParams : OutputExclusionParams
}
placeholder="Operator"
/>
</CCol>
<CCol sm="4">
<Select
onChange={setFieldValue}
// value={operator}
label="Operator"
name={`${targetHelper}[${key}].operator`}
options={SelectOptions}
placeholder="Operator"
/>
</CCol>
<CCol sm="4">
<Input
onChange={setFieldValue}
// value={value}
label="Value"
name={`${targetHelper}[${key}].value`}
type="text"
placeholder="Value"
/>
</CCol>
</StyledRow>
And just a guess, you didn't code all that by your self right? Because in one place you are doing exactly what you need to do to solve your problem. If you don't know how that name thing works with FieldArray, I recommend you reading this part of the formik docs. Know how this works is very important for using nested objects / arrays.
I have long registration form which I have divided into parts using steps component of AntD.
// Defined Child Form Components
steps = {
basicInfo: {
title: 'Basic Info',
content: (
<BasicInfoForm
form={this.props.form}
user={this.user}
/>
),
},
addresses: {
title: 'Addresses',
content: (
<Address
form={this.props.form}
addresses={this.user.addresses}
/>
),
},
contactInfo: {
title: 'Contact Info',
content: (
<PhoneForm
form={this.props.form}
contactInfo={this.user.contactInfo}
/>
),
},
}
// Form configuring these child form components
<Form onSubmit={handleSubmit} className="add-edit-user-form">
<Fragment>
<Steps progressDot current={this.state.currentStep}>
{Object.keys(steps).map((key, index) => {
return <Step title={steps && steps[index] ? steps[index].title : null} key={index} />;
})}
</Steps>
<div className={styles['steps-content']}>{steps && steps[currentStep] ? steps[currentStep].content : null}</div>
<div className="steps-action">
{currentStep > 0 && (
<Button style={{ marginLeft: 8 }} onClick={() => this.prev()}>Previous</Button>
)}
{currentStep < steps.length - 1 && (
<Button type="primary" htmlType="submit">Next</Button>
)}
{currentStep === steps.length - 1 && (
<Button type="primary" htmlType="submit">Done</Button>
)}
</div>
</Fragment>
</Form>
// class variable to user object coming from parent.
user = this.props.user ? this.props.user : undefined;
// Child component
interface IAddressFormProps {
form: WrappedFormUtils;
addresses: IAddress[];
fieldName: string;
}
class Address extends Component<IAddressFormProps> {
totalAddresses: any = this.props.addresses && this.props.addresses ?
this.props.addresses : ([{}] as IAddress[]);
state = {
totalAddresses: this.totalAddresses ? this.totalAddresses : ([] as IAddress[]),
};
addAddress = () => {
this.setState((prevState: any) => ({
totalAddresses: [...prevState.totalAddresses, { lineOne: '', lineTwo: '', lineThree: '', city: '', state: '', zip: '', type: '' }],
}));
};
removeAddress = (index: number) => {
this.setState({
totalAddresses: this.state.totalAddresses.filter((item: IAddress, addressIndex: number) => index !== addressIndex),
});
};
render() {
const { getFieldDecorator } = this.props.form;
const fieldName = this.props.fieldName;
return (
<Fragment>
{this.state.totalAddresses.map((item: IAddress, index: number) => {
return (
<Row key={'container-' + index} type="flex" justify="start" gutter={16}>
<Col span={6}>
<Form.Item label="Type" key={'type-' + index}>
{getFieldDecorator(`${fieldName}[${index}].type`, {
initialValue: item.type,
rules: [{ required: true, message: 'Please input address type!' }],
})(
<Select placeholder="Type">
<Option value="Mailing">Mailing</Option>
<Option value="Business">Business</Option>
<Option value="Home">Home</Option>
<Option value="Other">Other</Option>
</Select>
)}
</Form.Item>
<Form.Item label="Line One" key={'lineOne-' + index}>
{getFieldDecorator(`${fieldName}[${index}].lineOne`, {
initialValue: item.lineOne,
rules: [{ required: true, message: 'Please input line one!' }],
})(<Input placeholder="Line One" />)}
</Form.Item>
<Form.Item label="Line Two" key={'lineTwo-' + index}>
{getFieldDecorator(`${fieldName}[${index}].lineTwo`, {
initialValue: item.lineTwo,
rules: [{ required: false, message: 'Please input line two!' }],
})(<Input placeholder="Line Two" />)}
</Form.Item>
<Form.Item label="Line Three" key={'lineThree-' + index}>
{getFieldDecorator(`${fieldName}[${index}].lineThree`, {
initialValue: item.lineThree,
rules: [{ required: false, message: 'Please input line three!' }],
})(<Input placeholder="Line Three" />)}
</Form.Item>
</Col>
<Col span={9}>
<Form.Item label="City" key={'city-' + index}>
{getFieldDecorator(`${fieldName}[${index}].city`, {
initialValue: item.city,
rules: [{ required: true, message: 'Please input city!' }],
})(<Input placeholder="City" />)}
</Form.Item>
<Form.Item label="State" key={'state-' + index}>
{getFieldDecorator(`${fieldName}[${index}].state`, {
initialValue: item.state,
rules: [{ required: true, message: 'Please input state!' }],
})(<Input placeholder="State" />)}
</Form.Item>
<Form.Item label="Zip" key={'zip-' + index}>
{getFieldDecorator(`${fieldName}[${index}].zip`, {
initialValue: item.zip,
rules: [{ required: true, message: 'Please input zip!' }],
})(<Input placeholder="Zip" />)}
</Form.Item>
</Col>
<Col span={4}>
<Button onClick={() => this.removeAddress(index)}>Remove</Button>
</Col>
</Row>
);
})}
<Button onClick={() => this.addAddress()}>Add address</Button>
</Fragment>
);
}
}
I want to maintain the state of user object throughout the steps means going back and forth.
On form submit, updating the user object.
next = (addEditUser: MutationFn<any, any>) => {
const form = this.props.form;
form.validateFields(async (err: any, values: any) => {
if (err) {
return false;
}
values.id = this.userId;
let variables = this.parentId ? { user: values, parentId: this.parentId } : { user: values };
const result = await addEditUser({ variables: variables });
if (result && result.data) {
if (this.state.currentStep === this.state.steps.length - 1) {
this.props.history.push('/user');
} else {
this.user = Object.assign(this.user, values);
const currentStep = this.state.currentStep + 1;
this.setState({ currentStep });
}
}
});
};
The user object is correctly updated but the child components are not. Why?
Thanks in advance.
In order to pass props to the state you have to use the getDerivedStateFromProps method.
In your child class you can add the following - This is an example for your Address Component where you receive the address from the parent Component via props
static getDerivedStateFromProps(props, state) {
const { address } = props;
if(address !== state.address) return { address }
}
Now here what happens is that if the parent Component sends new props the function checks if the prop.address is different from the state.address - this is the child Component state - and if it's different it's sets it to the value received from the props
Add this method in your Address class and it should work
I was created a registration page using react. There I have used this following registration form. https://ant.design/components/form. All the validation were handled properly and after successful attempt user can register to the system. Only problem I have faced is, unable to clear the form input field values after the submission.
I have implemented a method to clear the form field values to null. But it was not working. And I tried out previous similar questions in stackoverflow, but still couldn't get a workable one for me. Reason for that may be since I am using the ant design template.
this.setState({
confirmDirty: false,
autoCompleteResult: [],
userName: '',
email: '',
experience: [],
password: ''
})
Above code is to clear the input values. But it was not working and the all the form input fields values remained save. Below is the part of the registration form code.
class RegistrationForm extends React.Component {
state = {
confirmDirty: false,
autoCompleteResult: [],
userName: '',
email: '',
experience: [],
password: ''
};
//below form is inside the render method and return
<Form onSubmit={this.handleSubmit}>
<FormItem
{...formItemLayout}
label="E-mail"
>
{getFieldDecorator('email', {
rules: [{
type: 'email', message: 'The input is not valid E-mail!',
}, {
required: true, message: 'Please input your E-mail!',
}],
})(
<Input />
)}
</FormItem>
</Form>
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
//submission done
//then execute the above code which I mentioned previously in the question, to reset the input fields value
}
});
}
}
Where I could get wrong and how can I resolve this?
In a function component, you can very easily get access to the form using the Form.useForm hook. The value returned by this hook should be passed as the form property of the Form component. Then you can just call resetFields on the form in whatever callback you want to clear the form. This example will clear the form when it is submitted:
import React from 'react';
import { Button, Form, Input } from 'antd';
export default function MyFormComponent() {
const [form] = Form.useForm();
const submitForm = ({ name, favoriteColor }) => {
console.log(name, favoriteColor);
form.resetFields();
};
return (
<Form
form={form}
labelCol={{ span: 6 }}
wrapperCol={{ span: 12 }}
onFinish={submitForm}
>
<Form.Item name="name" label="What is your name?">
<Input />
</Form.Item>
<Form.Item name="favoriteColor" label="What is your favorite color?">
<Input />
</Form.Item>
<Form.Item wrapperCol={{ offset: 6, span: 12 }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
}
This only works in antd 4.x and later. Forms seem to have been much more difficult to work with in earlier versions.
We can clear the form data using the resetFields function present in form props given by the ant design library.
After the form is submitted successfully, use this.props.form.resetFields() to clear the data in the form.
Code:
const { Form, Input, Tooltip, Icon, Cascader, Select, Row, Col, Checkbox, Button, AutoComplete, } = antd;
const { Option } = Select;
const AutoCompleteOption = AutoComplete.Option;
class RegistrationForm extends React.Component {
state = {
confirmDirty: false,
autoCompleteResult: [],
};
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
}
handleConfirmBlur = (e) => {
const value = e.target.value;
this.setState({ confirmDirty: this.state.confirmDirty || !!value });
}
render() {
const { getFieldDecorator } = this.props.form;
const { autoCompleteResult } = this.state;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
};
const tailFormItemLayout = {
wrapperCol: {
xs: {
span: 24,
offset: 0,
},
sm: {
span: 16,
offset: 8,
},
},
};
return (
<Form onSubmit={this.handleSubmit}>
<Form.Item
{...formItemLayout}
label="E-mail"
>
{getFieldDecorator('email', {
rules: [{
type: 'email', message: 'The input is not valid E-mail!',
}, {
required: true, message: 'Please input your E-mail!',
}],
})(
<Input />
)}
</Form.Item>
<Form.Item
{...formItemLayout}
label="Password"
>
{getFieldDecorator('password', {
rules: [{
required: true, message: 'Please input your password!',
}, {
validator: this.validateToNextPassword,
}],
})(
<Input type="password" />
)}
</Form.Item>
<Form.Item {...tailFormItemLayout}>
{getFieldDecorator('agreement', {
valuePropName: 'checked',
})(
<Checkbox>I have read the agreement</Checkbox>
)}
</Form.Item>
<Form.Item {...tailFormItemLayout}>
<Button type="primary" htmlType="submit">Register</Button>
</Form.Item>
<Form.Item {...tailFormItemLayout}>
<Button onClick={e => {
this.props.form.resetFields()
}} >Clear</Button>
</Form.Item>
</Form>
);
}
}
const WrappedRegistrationForm = Form.create()(RegistrationForm);
ReactDOM.render(<WrappedRegistrationForm />, mountNode);
Live Demo
Hope it helps :)
form.resetFields() will reset all form fields.
To reset a particular form field form.resetFields([formItemName])
FormInstance
import React, { useState } from "react";
import { Form, Input } from "antd";
const myComp = () => {
const [form] = Form.useForm();
const [val, setVal] = useState("");
const handleCancel = () => {
form.resetFields(); //reset form
};
const handleOk = async (e) => {
form.resetFields(); //reset form
};
const handelChange = async (e) => {
setVal(e.target.value);
if(val.length > 10) {
form.resetFields(["textField"]); //reset particular field
}
};
return (
<Form
form={form}
name="dynamic_ruleEdit"
onFinish={handleOk}
>
<Form.Item
name="textField"
label="Enter your details"
rules={[
{
message: "Enter details",
required: true,
},
]}
>
<Input
value={val}
placeholder="Enter details"
onChange={handelChange}
/>
</Form.Item>
</Form>
);
}
export default myComp;
Very Simple Solution:
Just put this line in the functional component
const [form] = Form.useForm();
<Form
form={form}
name="normal_login"
className="login-form"
initialValues={{
remember: true,
}}
/>
After than call the form into onFinish() function.
const onFinish=(values)=>{
form.resetFields();
let array1=[];
if(Array.isArray(array1)){
array1=values;
localStorage.setItem(`${id}`,JSON.stringify({id,...array1}));
}
}
Create a new Button and set the onclick().
Then call this.props.form.resetFields() in onclick of the button
which you created above.
For this whole you need to export your component as export default
Form.create()(YourComponent);
the simplest way to reset the entire form you use resetForm().
form.resetFields()
For those inputs you want to change the default value, you can specify it using setFieldsValue().
...
onChange={() => form.setFieldsValue({ name: '' })}
...
I'm using antd and I wanna use its registration Form in my project.
Note: Before adding this component my project (registration.js) was working correctly and I've just copied and pasted the antd component.
Here is my registration.js file:
import { Form, Input, Tooltip, Icon, Cascader, Select, Row, Col, Checkbox, Button, AutoComplete } from 'antd';
const FormItem = Form.Item;
const Option = Select.Option;
const AutoCompleteOption = AutoComplete.Option;
class RegistrationForm extends React.Component {
state = {
confirmDirty: false,
autoCompleteResult: [],
};
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
}
handleConfirmBlur = (e) => {
const value = e.target.value;
this.setState({ confirmDirty: this.state.confirmDirty || !!value });
}
compareToFirstPassword = (rule, value, callback) => {
const form = this.props.form;
if (value && value !== form.getFieldValue('password')) {
callback('Two passwords that you enter is inconsistent!');
} else {
callback();
}
}
validateToNextPassword = (rule, value, callback) => {
const form = this.props.form;
if (value && this.state.confirmDirty) {
form.validateFields(['confirm'], { force: true });
}
callback();
}
handleWebsiteChange = (value) => {
let autoCompleteResult;
if (!value) {
autoCompleteResult = [];
} else {
autoCompleteResult = ['.com', '.org', '.net'].map(domain => `${value}${domain}`);
}
this.setState({ autoCompleteResult });
}
render() {
const { getFieldDecorator } = this.props.form;
const { autoCompleteResult } = this.state;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
};
const tailFormItemLayout = {
wrapperCol: {
xs: {
span: 24,
offset: 0,
},
sm: {
span: 16,
offset: 8,
},
},
};
const prefixSelector = getFieldDecorator('prefix', {
initialValue: '86',
})(
<Select style={{ width: 70 }}>
<Option value="86">+86</Option>
<Option value="87">+87</Option>
</Select>
);
const websiteOptions = autoCompleteResult.map(website => (
<AutoCompleteOption key={website}>{website}</AutoCompleteOption>
));
return (
<Form onSubmit={this.handleSubmit}>
<FormItem
{...formItemLayout}
label="E-mail"
>
{getFieldDecorator('email', {
rules: [{
type: 'email', message: 'The input is not valid E-mail!',
}, {
required: true, message: 'Please input your E-mail!',
}],
})(
<Input />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Password"
>
{getFieldDecorator('password', {
rules: [{
required: true, message: 'Please input your password!',
}, {
validator: this.validateToNextPassword,
}],
})(
<Input type="password" />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Confirm Password"
>
{getFieldDecorator('confirm', {
rules: [{
required: true, message: 'Please confirm your password!',
}, {
validator: this.compareToFirstPassword,
}],
})(
<Input type="password" onBlur={this.handleConfirmBlur} />
)}
</FormItem>
<FormItem
{...formItemLayout}
label={(
<span>
Nickname
<Tooltip title="What do you want others to call you?">
<Icon type="question-circle-o" />
</Tooltip>
</span>
)}
>
{getFieldDecorator('nickname', {
rules: [{ required: true, message: 'Please input your nickname!', whitespace: true }],
})(
<Input />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Habitual Residence"
>
{getFieldDecorator('residence', {
initialValue: ['zhejiang', 'hangzhou', 'xihu'],
rules: [{ type: 'array', required: true, message: 'Please select your habitual residence!' }],
})(
<Cascader options={residences} />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Phone Number"
>
{getFieldDecorator('phone', {
rules: [{ required: true, message: 'Please input your phone number!' }],
})(
<Input addonBefore={prefixSelector} style={{ width: '100%' }} />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Website"
>
{getFieldDecorator('website', {
rules: [{ required: true, message: 'Please input website!' }],
})(
<AutoComplete
dataSource={websiteOptions}
onChange={this.handleWebsiteChange}
placeholder="website"
>
<Input />
</AutoComplete>
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Captcha"
extra="We must make sure that your are a human."
>
<Row gutter={8}>
<Col span={12}>
{getFieldDecorator('captcha', {
rules: [{ required: true, message: 'Please input the captcha you got!' }],
})(
<Input />
)}
</Col>
<Col span={12}>
<Button>Get captcha</Button>
</Col>
</Row>
</FormItem>
<FormItem {...tailFormItemLayout}>
{getFieldDecorator('agreement', {
valuePropName: 'checked',
})(
<Checkbox>I have read the agreement</Checkbox>
)}
</FormItem>
<FormItem {...tailFormItemLayout}>
<Button type="primary" htmlType="submit">Register</Button>
</FormItem>
</Form>
);
}
}
const WrappedRegistrationForm = Form.create()(RegistrationForm);
ReactDOM.render(<WrappedRegistrationForm />, document.getElementById('main'));
And this is my index.html file:
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>React Project</title>
</head>
<body>
<div id="main"></div>
<script src="/build/bundle.js"></script>
</body>
</html>
I face with this error in my registration.js file:
Unexpected token (8:8) while parsing file
Line 8 refers to where we have state = {, how can I solve this problem?
Probably you are not using the right transpilers.
Declaring global variables in classes like your state = { ... } is only possible with ES7 (I believe). You are probably using a ES6 transpiler.
One fix would be changing your syntax to ES6. So you would have to declare you state like this:
class RegistrationForm extends React.Component {
constructor(props) {
super(props);
this.state = { ... };
}
// ...
}
ES6 also doesn't support declaring class methods with arrow syntax (myMethod = () => { ... }). You would have to change all of your methods to myMethod() { ... }
Another fix would be to download the right presets so babel (I'm guessing you are using babel) can transpile your ES7 syntax.